feat(upserver): 保存M2客户端KV迁移至本人统计写检查点
保存独立KV用户服务、固定会话存档与原业务接线,以及C3d资料缓存和C3e本人统计写。 保留原PF/AGG路线、业务公式和时序;榜单读取、好友资料、公告、匹配及首场景闸仍待完成。 验证:ServiceHost两条件各33组646断言;Unity CLI Loop编译0错误269警告。 包含相关方案、评审裁决、测试及状态文档。未操作服务器、正式账号或打包。
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Activity.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用本次登录绑定的存档提供方,保留活动类型名作为存档键。
|
||||
/// </summary>
|
||||
public class EventDataRepository : IEventDataRepository
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore store;
|
||||
public EventDataRepository(game.PlayerDataStorage.IPlayerSaveStore store)
|
||||
{ this.store = store ?? throw new ArgumentNullException(nameof(store)); }
|
||||
public void Save<T>(T data) where T : IEventData
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
Debug.LogWarning($"[EventDataRepository] Cannot save null data for {typeof(T).Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(data);
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"[EventDataRepository] Saving {typeof(T).Name}: {json}");
|
||||
#endif
|
||||
store.Write(typeof(T).Name, json).RequireAccepted();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventDataRepository] Failed to save {typeof(T).Name}: {e.Message}\n{e.StackTrace}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public T Load<T>() where T : IEventData, new()
|
||||
{
|
||||
try
|
||||
{
|
||||
var read = store.Read(typeof(T).Name);
|
||||
var dataStr = read.ValueOrThrow();
|
||||
if (read.Found == false) return new T();
|
||||
if (string.IsNullOrWhiteSpace(dataStr) || dataStr.Trim() == "null")
|
||||
throw new JsonSerializationException("Stored activity data cannot be empty or null.");
|
||||
// #if UNITY_EDITOR
|
||||
// Debug.Log($"[EventDataRepository] Load {typeof(T).Name}: {dataStr}");
|
||||
// #endif
|
||||
var data = JsonConvert.DeserializeObject<T>(dataStr);
|
||||
if (data == null) throw new JsonSerializationException("Stored activity data cannot be null.");
|
||||
return data;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventDataRepository] Failed to load {typeof(T).Name}: {e.Message}\n{e.StackTrace}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Exists<T>() where T : IEventData
|
||||
{
|
||||
var read = store.Read(typeof(T).Name);
|
||||
read.ValueOrThrow();
|
||||
return read.Found.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using asap.core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Activity
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="Services.IEventTokenService"/> 入账/扣账成功后由 <see cref="Services.EventTokenService"/> 发布,
|
||||
/// 供各活动自行判断是否刷新入口合并红点等(避免订阅者与具体玩法耦合)。
|
||||
/// </summary>
|
||||
public readonly struct ActivityEventTokenBalanceChanged
|
||||
{
|
||||
public int EventId { get; }
|
||||
|
||||
public ActivityEventTokenBalanceChanged(int eventId)
|
||||
{
|
||||
EventId = eventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Activity.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 活动代币服务实现
|
||||
/// 负责管理所有活动的代币数据,提供统一的存储接口
|
||||
/// </summary>
|
||||
public class EventTokenService : IEventTokenService
|
||||
{
|
||||
#region 常量与字段
|
||||
|
||||
private const string TransitionDataKey = "TransitionData";
|
||||
|
||||
private Dictionary<int, int> _tokenData = new Dictionary<int, int>();
|
||||
private readonly object _lock = new object();
|
||||
private bool _isInitialized;
|
||||
private readonly game.PlayerDataStorage.IPlayerSaveStore store;
|
||||
public EventTokenService(game.PlayerDataStorage.IPlayerSaveStore store)
|
||||
{ this.store = store ?? throw new ArgumentNullException(nameof(store)); }
|
||||
#endregion
|
||||
|
||||
#region 初始化
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (!store.IsCurrent) throw new InvalidOperationException("Activity token session is no longer current.");
|
||||
if (_isInitialized) return;
|
||||
var read = store.Read(TransitionDataKey);
|
||||
var value = read.ValueOrThrow();
|
||||
Dictionary<int, int> loaded;
|
||||
if (read.Found == false) loaded = new Dictionary<int, int>();
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
throw new Newtonsoft.Json.JsonSerializationException("Stored activity tokens cannot be empty.");
|
||||
loaded = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<int, int>>(value);
|
||||
if (loaded == null) throw new Newtonsoft.Json.JsonSerializationException("Stored activity tokens cannot be null.");
|
||||
}
|
||||
_tokenData = loaded;
|
||||
_isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Publish the candidate balance only after the current store accepts its exact JSON.
|
||||
// Acceptance remains distinct from cloud confirmation, which belongs to the enclosing action.
|
||||
private void SaveTokens(Dictionary<int, int> candidate)
|
||||
{
|
||||
store.Write(TransitionDataKey, Newtonsoft.Json.JsonConvert.SerializeObject(candidate)).RequireAccepted();
|
||||
_tokenData = candidate;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 基础操作
|
||||
|
||||
public void AddToken(int eventId, int count)
|
||||
{
|
||||
if (eventId <= 0)
|
||||
{
|
||||
Debug.LogWarning($"[EventTokenService] Invalid eventId: {eventId}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
var currentCount = GetTokenCount(eventId);
|
||||
var candidate = new Dictionary<int, int>(_tokenData);
|
||||
candidate[eventId] = currentCount + count;
|
||||
SaveTokens(candidate);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"[EventTokenService] Added {count} tokens to event {eventId}, new total: {_tokenData[eventId]}");
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] AddToken failed for event {eventId}: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
PublishActivityEventTokenBalanceChanged(eventId);
|
||||
}
|
||||
|
||||
public int RemoveToken(int eventId, int count)
|
||||
{
|
||||
if (eventId <= 0)
|
||||
{
|
||||
Debug.LogWarning($"[EventTokenService] Invalid eventId: {eventId}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int actualRemoved = 0;
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
var currentCount = GetTokenCount(eventId);
|
||||
actualRemoved = Mathf.Min(count, currentCount);
|
||||
var newCount = currentCount - actualRemoved;
|
||||
|
||||
var candidate = new Dictionary<int, int>(_tokenData);
|
||||
if (newCount <= 0)
|
||||
{
|
||||
candidate.Remove(eventId);
|
||||
}
|
||||
else
|
||||
{
|
||||
candidate[eventId] = newCount;
|
||||
}
|
||||
|
||||
SaveTokens(candidate);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"[EventTokenService] Removed {actualRemoved} tokens from event {eventId}, remaining: {newCount}");
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] RemoveToken failed for event {eventId}: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
PublishActivityEventTokenBalanceChanged(eventId);
|
||||
|
||||
return actualRemoved;
|
||||
}
|
||||
|
||||
public int GetTokenCount(int eventId)
|
||||
{
|
||||
if (eventId <= 0) return 0;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _tokenData.GetValueOrDefault(eventId, 0);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] GetTokenCount failed for event {eventId}: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetToken(int eventId, int count)
|
||||
{
|
||||
if (eventId <= 0)
|
||||
{
|
||||
Debug.LogWarning($"[EventTokenService] Invalid eventId: {eventId}");
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
var candidate = new Dictionary<int, int>(_tokenData);
|
||||
candidate[eventId] = count;
|
||||
SaveTokens(candidate);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"[EventTokenService] Set event {eventId} token count to {count}");
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] SetToken failed for event {eventId}: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearToken(int eventId)
|
||||
{
|
||||
if (eventId <= 0) return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
var candidate = new Dictionary<int, int>(_tokenData);
|
||||
if (candidate.Remove(eventId))
|
||||
{
|
||||
SaveTokens(candidate);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] ClearToken failed for event {eventId}: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 批量操作
|
||||
|
||||
public Dictionary<int, int> GetAllTokens()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return new Dictionary<int, int>(_tokenData);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTokens(Dictionary<int, int> tokens)
|
||||
{
|
||||
if (tokens == null) return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
var candidate = new Dictionary<int, int>();
|
||||
foreach (var kvp in tokens)
|
||||
{
|
||||
if (kvp.Key > 0)
|
||||
{
|
||||
candidate[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
SaveTokens(candidate);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"[EventTokenService] Set {tokens.Count} token entries");
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] SetTokens failed: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearTokens(IEnumerable<int> eventIds)
|
||||
{
|
||||
if (eventIds == null) return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureInitialized();
|
||||
var candidate = new Dictionary<int, int>(_tokenData);
|
||||
bool changed = false;
|
||||
foreach (var eventId in eventIds)
|
||||
{
|
||||
if (candidate.Remove(eventId))
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
SaveTokens(candidate);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventTokenService] ClearTokens failed: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有辅助方法
|
||||
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private static void PublishActivityEventTokenBalanceChanged(int eventId)
|
||||
{
|
||||
try
|
||||
{
|
||||
GContext.Publish(new Activity.ActivityEventTokenBalanceChanged(eventId));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// GContext 未就绪等
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,700 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
public class AquariumManager
|
||||
{
|
||||
public const string AquariumEventDataNewKey = "AquariumEventDataNewKey";
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
readonly IUserService loginUser;
|
||||
private cfg.Tables _tables;
|
||||
private ICustomServerMgr customServerMgr;
|
||||
public AquariumManagerData data { get; private set; }
|
||||
public int MapId => data.mapId;
|
||||
public int Slot => data.slot;
|
||||
public List<string> eggNames;
|
||||
public List<AquariumSlotData> sellList;
|
||||
public AquariumManager()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
loginUser = GContext.container.Resolve<IUserService>();
|
||||
_tables = GContext.container.Resolve<Tables>();
|
||||
customServerMgr = GContext.container.Resolve<ICustomServerMgr>();
|
||||
}
|
||||
public static AquariumManagerData GetAquariumManagerData(game.PlayerDataStorage.IPlayerSaveStore store = null)
|
||||
{
|
||||
var source = store ?? GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
string dataStr = source.Read(AquariumEventDataNewKey).ValueOrThrow();
|
||||
if (!string.IsNullOrEmpty(dataStr))
|
||||
{
|
||||
try
|
||||
{
|
||||
return AquariumManagerData.Deserialize(dataStr);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"GetAquariumManagerData Deserialize Error:{e}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public async Task Synchrodata()
|
||||
{
|
||||
//从服务器拿数据
|
||||
if (!saveStore.IsCurrent) throw new OperationCanceledException();
|
||||
var dataSever = GetAquariumManagerData(saveStore);
|
||||
if (dataSever == null)
|
||||
{
|
||||
//新数据
|
||||
data = new AquariumManagerData();
|
||||
data.startTime = ZZTimeHelper.UtcNow().AddDays(-2);
|
||||
data.refreshCount = 0;
|
||||
data.adCount = 0;
|
||||
data.refreshDay = ZZTimeHelper.UtcNow().UtcNowOffset().Date.DayOfYear;
|
||||
|
||||
data.slot = GContext.container.Resolve<PlayerData>().benefitsAquariumLimit;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = dataSever;
|
||||
if (data.slotDatas == null || data.slotDatas.Count < data.slot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
GetAquariumDataRequest request = new GetAquariumDataRequest();
|
||||
request.playFabId = loginUser.UserId;
|
||||
string result = await customServerMgr.CustomServerPost("aquarium/GetAquariumData", Newtonsoft.Json.JsonConvert.SerializeObject(request));
|
||||
if (!saveStore.IsCurrent) throw new OperationCanceledException();
|
||||
AquariumServerData serverData = null;
|
||||
if (result != null)
|
||||
{
|
||||
var resp = JsonConvert.DeserializeObject<GetAquariumDataResp>(result);
|
||||
if (resp.code == 0)
|
||||
{
|
||||
serverData = resp.data;
|
||||
}
|
||||
else if (resp.code == 1)
|
||||
{
|
||||
Debug.LogWarning("GetAquariumDataRequest Null");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("GetAquariumDataRequest Null");
|
||||
}
|
||||
}
|
||||
if (serverData != null)
|
||||
{
|
||||
data.aquariumHurtDatas = serverData.aquariumHurtDatas ?? new List<AquariumHurtData>();
|
||||
int count = data.slot - data.aquariumHurtDatas.Count;
|
||||
if (count > 0)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
AquariumHurtData aquariumHurtData = new AquariumHurtData();
|
||||
data.aquariumHurtDatas.Add(aquariumHurtData);
|
||||
}
|
||||
}
|
||||
data.aquariumResultDatas = serverData.aquariumResultDatas ?? new List<AquariumResultData>();
|
||||
count = data.slot - data.aquariumResultDatas.Count;
|
||||
if (count > 0)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
AquariumResultData aquariumResultData = new AquariumResultData();
|
||||
data.aquariumResultDatas.Add(aquariumResultData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetJury();
|
||||
//保存 数据
|
||||
SaveData(serverData == null);
|
||||
}
|
||||
}
|
||||
void SetJury()
|
||||
{
|
||||
var dateTime = ZZTimeHelper.UtcNow();
|
||||
AquariumFish aquariumFish = null;
|
||||
for (int i = 0; i < data.slot; i++)
|
||||
{
|
||||
AquariumSlotData slotData = data.slotDatas[i];
|
||||
slotData.index = i;
|
||||
AquariumHurtData aquariumHurtData = data.aquariumHurtDatas[i];
|
||||
//没有受新伤
|
||||
if (aquariumHurtData.injuryTime < slotData.allRecoveryTime + 0.0001f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var HatchTime = slotData.FishHatchTime();
|
||||
//还没孵化
|
||||
if (HatchTime > dateTime)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//检查是否受伤
|
||||
//正常的成长时间 自然时间+加速成长-总恢复时间
|
||||
float normalH = (float)(dateTime - HatchTime).TotalHours + slotData.accelerationGrowth - slotData.allRecoveryTime;
|
||||
//受伤时的成长时间 受伤时的时间+加速成长-受伤时的总恢复时间
|
||||
float injuryH = slotData.injuryGrowthTime;
|
||||
|
||||
aquariumFish = _tables.TbAquariumFish.GetOrDefault(slotData.aquariumFishID);
|
||||
//受伤时的成长进度大于正常的成长进度=还没恢复
|
||||
if (injuryH > normalH)
|
||||
{
|
||||
var recoveryTime = UnityEngine.Random.Range(aquariumFish.RecoveryTime[0], aquariumFish.RecoveryTime[1]);
|
||||
slotData.recoveryTime = recoveryTime;
|
||||
recoveryTime -= injuryH - normalH;
|
||||
//处于受伤状态刷新时间
|
||||
if (recoveryTime > 0)
|
||||
{
|
||||
slotData.allRecoveryTime += recoveryTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//不处于受伤状态添加受伤时间
|
||||
//当前正常的成长进度 自然时间+加速成长 - 全部恢复时间
|
||||
//受伤时的成长时间 受伤时的时间+加速成长-受伤时的总恢复时间
|
||||
|
||||
slotData.injuryGrowthTime = (float)(dateTime - HatchTime).TotalHours + slotData.accelerationGrowth - slotData.allRecoveryTime;
|
||||
slotData.recoveryTime = UnityEngine.Random.Range(aquariumFish.RecoveryTime[0], aquariumFish.RecoveryTime[1]);
|
||||
slotData.allRecoveryTime += slotData.recoveryTime;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public void GetRandmMap()
|
||||
{
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
|
||||
data.startTime = ZZTimeHelper.UtcNow();
|
||||
data.slot = playerData.benefitsAquariumLimit;
|
||||
List<AquariumInit> inits = _tables.TbAquariumInit.DataList;
|
||||
if (data.startCount < inits.Count)
|
||||
{
|
||||
NewSlotInitData(inits[data.startCount]);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewSlotData();
|
||||
}
|
||||
data.refreshCount++;
|
||||
data.isHome = false;
|
||||
data.startCount++;
|
||||
//保存 数据
|
||||
SaveData(true);
|
||||
GContext.container.Resolve<TriggerPackData>().RefreshTriggerPackData(4000201, data.mapId);
|
||||
|
||||
#if AGG
|
||||
int fishcard_totallevel = playerFishData.GetFishLevelByMap(data.mapId);
|
||||
string egg_quality_list = data.slotDatas[0].eggId.ToString();
|
||||
for (int i = 1; i < data.slotDatas.Count; i++)
|
||||
{
|
||||
egg_quality_list = $"{egg_quality_list},{data.slotDatas[i].eggId}";
|
||||
}
|
||||
using (var e = GEvent.GameEvent("aquarium_start"))
|
||||
{
|
||||
e.AddContent("map_id", data.mapId)
|
||||
.AddContent("total_eggpots", data.slot)
|
||||
.AddContent("fishcard_totallevel", fishcard_totallevel)
|
||||
.AddContent("egg_quality_list", egg_quality_list);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void NewSlotInitData(AquariumInit init)
|
||||
{
|
||||
data.mapId = init.MapID;
|
||||
List<AquariumSlotData> slotDatas = new List<AquariumSlotData>();
|
||||
List<AquariumHurtData> aquariumHurtDatas = new List<AquariumHurtData>();
|
||||
List<AquariumResultData> aquariumResultDatas = new List<AquariumResultData>();
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow();
|
||||
|
||||
List<AquariumEgg> TankEggList = _tables.TbAquariumEgg.DataList;
|
||||
eggNames = new List<string>();
|
||||
for (int i = 0; i < TankEggList.Count; i++)
|
||||
{
|
||||
eggNames.Add(TankEggList[i].Icon);
|
||||
}
|
||||
|
||||
List<int> Egglist = init.Egglist;
|
||||
List<int> FishQualitylist = init.FishQualitylist;
|
||||
List<int> FishList = init.Fishlist;
|
||||
List<int> FishQuatitylist = init.FishQuatitylist;
|
||||
List<int> FishMutantlist = init.FishMutantlist;
|
||||
|
||||
|
||||
for (int i = 0; i < data.slot; i++)
|
||||
{
|
||||
AquariumSlotData aquariumSlotData = new AquariumSlotData();
|
||||
aquariumSlotData.eggId = Egglist[i];
|
||||
aquariumSlotData.aquariumFishID = FishQualitylist[i];
|
||||
aquariumSlotData.fishMQ = FishQuatitylist[i];
|
||||
aquariumSlotData.fishItemID = FishList[i];
|
||||
|
||||
var tankEgg = _tables.TbAquariumEgg.GetOrDefault(Egglist[i]);
|
||||
|
||||
aquariumSlotData.startTime = dateTime;
|
||||
aquariumSlotData.hatchTime = UnityEngine.Random.Range(tankEgg.HatchTime[0], tankEgg.HatchTime[1]);
|
||||
|
||||
AquariumFish aquariumFish = _tables.TbAquariumFish.GetOrDefault(FishQualitylist[i]);
|
||||
|
||||
float allCash = SetRewardCount(aquariumFish.SoldReward);
|
||||
float soldRewardRand = UnityEngine.Random.Range(0, aquariumFish.SoldRewardRand) - aquariumFish.SoldRewardRand * 0.5f;
|
||||
aquariumSlotData.allCash = allCash * (1 + soldRewardRand);
|
||||
|
||||
aquariumSlotData.growthTime = UnityEngine.Random.Range(aquariumFish.GrowthTime[0], aquariumFish.GrowthTime[1]);
|
||||
aquariumSlotData.mutants = FishMutantlist[i];
|
||||
aquariumSlotData.index = i;
|
||||
slotDatas.Add(aquariumSlotData);
|
||||
|
||||
AquariumHurtData aquariumHurtData = new AquariumHurtData();
|
||||
aquariumHurtDatas.Add(aquariumHurtData);
|
||||
|
||||
AquariumResultData aquariumResultData = new AquariumResultData();
|
||||
aquariumResultDatas.Add(aquariumResultData);
|
||||
}
|
||||
data.slotDatas = slotDatas;
|
||||
data.aquariumHurtDatas = aquariumHurtDatas;
|
||||
data.aquariumResultDatas = aquariumResultDatas;
|
||||
}
|
||||
|
||||
void SetAquariumFish(MapData mapData, AquariumSlotData aquariumSlotData, AquariumEgg tankEgg)
|
||||
{
|
||||
List<FishItemList> fishList = mapData.DropFishList;
|
||||
|
||||
//权重取鱼
|
||||
int allProb = tankEgg.HatchFishQualityProb.Sum();
|
||||
int prob = UnityEngine.Random.Range(0, allProb);
|
||||
int curProb = 0;
|
||||
int fishQuality = tankEgg.HatchFishQuality[^1];
|
||||
for (int k = 0; k < tankEgg.HatchFishQuality.Count; k++)
|
||||
{
|
||||
curProb += tankEgg.HatchFishQualityProb[k];
|
||||
if (curProb > prob)
|
||||
{
|
||||
fishQuality = tankEgg.HatchFishQuality[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
//品质大于6???
|
||||
if (6 > fishQuality)
|
||||
{
|
||||
List<int> FishIDList = fishList[fishQuality - 1].FishIDList;
|
||||
aquariumSlotData.fishItemID = FishIDList[UnityEngine.Random.Range(0, FishIDList.Count)];
|
||||
}
|
||||
else
|
||||
{
|
||||
List<int> FishIDList = _tables.TbAquariumConfig.AquariumSpFishList;
|
||||
aquariumSlotData.fishItemID = FishIDList[UnityEngine.Random.Range(0, FishIDList.Count)];
|
||||
}
|
||||
aquariumSlotData.aquariumFishID = fishQuality;
|
||||
//取鱼的数量
|
||||
allProb = tankEgg.HatchFishQuantityProb.Sum();
|
||||
prob = UnityEngine.Random.Range(0, allProb);
|
||||
curProb = 0;
|
||||
int fishCount = tankEgg.HatchFishQuantity[0];
|
||||
for (int k = 0; k < tankEgg.HatchFishQuantity.Count; k++)
|
||||
{
|
||||
curProb += tankEgg.HatchFishQuantityProb[k];
|
||||
if (curProb > prob)
|
||||
{
|
||||
fishCount = tankEgg.HatchFishQuantity[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
aquariumSlotData.fishMQ = fishCount;
|
||||
}
|
||||
|
||||
void SetTime(AquariumSlotData aquariumSlotData, AquariumEgg tankEgg)
|
||||
{
|
||||
aquariumSlotData.hatchTime = UnityEngine.Random.Range(tankEgg.HatchTime[0], tankEgg.HatchTime[1]);
|
||||
|
||||
AquariumFish aquariumFish = _tables.TbAquariumFish.GetOrDefault(aquariumSlotData.aquariumFishID);
|
||||
|
||||
float allCash = SetRewardCount(aquariumFish.SoldReward);
|
||||
float soldRewardRand = UnityEngine.Random.Range(0, aquariumFish.SoldRewardRand) - aquariumFish.SoldRewardRand * 0.5f;
|
||||
aquariumSlotData.allCash = allCash * (1 + soldRewardRand);
|
||||
|
||||
aquariumSlotData.growthTime = UnityEngine.Random.Range(aquariumFish.GrowthTime[0], aquariumFish.GrowthTime[1]);
|
||||
aquariumSlotData.mutants = 0;
|
||||
for (int j = 0; j < aquariumSlotData.fishMQ; j++)
|
||||
{
|
||||
if (UnityEngine.Random.Range(0, 1f) < aquariumFish.MutantProb)
|
||||
{
|
||||
aquariumSlotData.mutants++;
|
||||
}
|
||||
}
|
||||
}
|
||||
void NewSlotData()
|
||||
{
|
||||
int curMapId = MapId;
|
||||
|
||||
var mapDatas = _tables.TbMapData.DataList;
|
||||
var maxMapID = GContext.container.Resolve<PlayerData>().lastMapId;
|
||||
|
||||
//非当前地图
|
||||
var mapIDs = new List<int>();
|
||||
for (int i = 0; i < mapDatas.Count; i++)
|
||||
{
|
||||
if (mapDatas[i].ID <= maxMapID)
|
||||
{
|
||||
if (mapDatas[i].ID != curMapId)
|
||||
{
|
||||
mapIDs.Add(mapDatas[i].ID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mapIDs.Count == 0)
|
||||
{
|
||||
data.mapId = maxMapID;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.mapId = mapIDs[UnityEngine.Random.Range(0, mapIDs.Count)];
|
||||
}
|
||||
|
||||
List<AquariumSlotData> slotDatas = new List<AquariumSlotData>();
|
||||
List<AquariumHurtData> aquariumHurtDatas = new List<AquariumHurtData>();
|
||||
List<AquariumResultData> aquariumResultDatas = new List<AquariumResultData>();
|
||||
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow();
|
||||
MapData mapData = _tables.TbMapData.GetOrDefault(data.mapId);
|
||||
List<AquariumEgg> TankEggList = _tables.TbAquariumEgg.DataList;
|
||||
float eggAllWeight = -0.0001f;
|
||||
eggNames = new List<string>();
|
||||
for (int i = 0; i < TankEggList.Count; i++)
|
||||
{
|
||||
eggNames.Add(TankEggList[i].Icon);
|
||||
eggAllWeight += TankEggList[i].Weight;
|
||||
}
|
||||
for (int i = 0; i < data.slot; i++)
|
||||
{
|
||||
AquariumSlotData aquariumSlotData = new AquariumSlotData();
|
||||
//权重取蛋
|
||||
float eggWeight = UnityEngine.Random.Range(0, eggAllWeight);
|
||||
float curEggWeight = 0;
|
||||
AquariumEgg tankEgg = TankEggList[0];
|
||||
foreach (var item in TankEggList)
|
||||
{
|
||||
curEggWeight += item.Weight;
|
||||
if (curEggWeight > eggWeight)
|
||||
{
|
||||
tankEgg = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
aquariumSlotData.eggId = tankEgg.ID;
|
||||
SetAquariumFish(mapData, aquariumSlotData, tankEgg);
|
||||
aquariumSlotData.startTime = dateTime;
|
||||
SetTime(aquariumSlotData, tankEgg);
|
||||
aquariumSlotData.index = i;
|
||||
slotDatas.Add(aquariumSlotData);
|
||||
|
||||
AquariumHurtData aquariumHurtData = new AquariumHurtData();
|
||||
aquariumHurtDatas.Add(aquariumHurtData);
|
||||
|
||||
AquariumResultData aquariumResultData = new AquariumResultData();
|
||||
aquariumResultDatas.Add(aquariumResultData);
|
||||
}
|
||||
data.slotDatas = slotDatas;
|
||||
data.aquariumHurtDatas = aquariumHurtDatas;
|
||||
data.aquariumResultDatas = aquariumResultDatas;
|
||||
}
|
||||
|
||||
public void Speedup(bool isGuide)
|
||||
{
|
||||
var dateTime = ZZTimeHelper.UtcNow();
|
||||
float AdAccelerate = _tables.TbAquariumConfig.AdAccelerate;
|
||||
if (isGuide)
|
||||
{
|
||||
AdAccelerate = _tables.TbAquariumConfig.AdAccelerateFirst;
|
||||
}
|
||||
List<AquariumSlotData> slotDataList = data.slotDatas;
|
||||
List<AquariumResultData> aquariumReportDatas = data.aquariumResultDatas;
|
||||
for (int i = 0; i < slotDataList.Count; i++)
|
||||
{
|
||||
var slotData = slotDataList[i];
|
||||
|
||||
//售卖或者丢失
|
||||
if (slotData.IsResult() || aquariumReportDatas[slotData.index].IsResult())
|
||||
{ continue; }
|
||||
var HatchTime = slotData.FishHatchTime();
|
||||
if (HatchTime <= dateTime)
|
||||
{
|
||||
//鱼加速
|
||||
slotData.accelerationGrowth += AdAccelerate;
|
||||
}
|
||||
else
|
||||
{
|
||||
float s = (float)(HatchTime - dateTime).TotalHours;
|
||||
if (s <= 0)
|
||||
{
|
||||
slotData.accelerationGrowth += AdAccelerate;
|
||||
}
|
||||
else if (AdAccelerate > s)
|
||||
{
|
||||
slotData.accelerationHatch += s;
|
||||
slotData.accelerationGrowth += AdAccelerate - s;
|
||||
}
|
||||
else
|
||||
{
|
||||
slotData.accelerationHatch += AdAccelerate;
|
||||
}
|
||||
}
|
||||
}
|
||||
data.adCount++;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public void SellFish(List<AquariumSlotData> slotDataList)
|
||||
{
|
||||
sellList = new List<AquariumSlotData>();
|
||||
var dateTime = ZZTimeHelper.UtcNow();
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
var weight = GetWeightMag();
|
||||
AquariumFish aquariumFish;
|
||||
FishData fishData;
|
||||
DateTime startTime;
|
||||
float injuryH;
|
||||
float weightIncEvent;
|
||||
List<ItemData> itemDatas = new List<ItemData>();
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
float CashMag = (1 + playerData.benefitsCashMag) * (1 + playerData.benefitsAquariumCashMag);
|
||||
for (int i = 0; i < slotDataList.Count; i++)
|
||||
{
|
||||
var slotData = slotDataList[i];
|
||||
sellList.Add(slotData);
|
||||
if (slotData.allCash == 0)
|
||||
{
|
||||
slotData.allCash = 1;
|
||||
}
|
||||
//奖励
|
||||
aquariumFish = _tables.TbAquariumFish.GetOrDefault(slotData.aquariumFishID);
|
||||
startTime = slotData.FishHatchTime();
|
||||
injuryH = slotData.injuryGrowthTime;
|
||||
weightIncEvent = (1 + weight[slotData.aquariumFishID - 1]) * CashMag;
|
||||
|
||||
float normalH = (float)(dateTime - startTime).TotalHours + slotData.accelerationGrowth - slotData.allRecoveryTime;
|
||||
|
||||
float rewardProgress = normalH / slotData.growthTime;
|
||||
|
||||
if (injuryH > normalH && injuryH > slotData.growthTime)
|
||||
{
|
||||
rewardProgress = 1 - ((injuryH - normalH) / slotData.growthTime);
|
||||
}
|
||||
|
||||
if (rewardProgress > 1)
|
||||
{
|
||||
rewardProgress = 1;
|
||||
}
|
||||
|
||||
//金币显示
|
||||
ItemData itemData = new ItemData();
|
||||
|
||||
int fishQ = slotData.fishMQ;
|
||||
bool is_injured = false;
|
||||
if (injuryH > normalH)
|
||||
{
|
||||
fishQ -= data.aquariumHurtDatas[slotData.index].fishLQ;
|
||||
is_injured = true;
|
||||
if (fishQ <= 0)
|
||||
{
|
||||
fishQ = 1;
|
||||
}
|
||||
}
|
||||
if (rewardProgress < 0)
|
||||
{
|
||||
rewardProgress = 0;
|
||||
}
|
||||
for (int j = 0; j < fishQ; j++)
|
||||
{
|
||||
if (j < slotData.mutants)
|
||||
{
|
||||
itemData.count += (int)(slotData.allCash * (1 + aquariumFish.MutantGrowthExtraSoldReward * rewardProgress) * (1 + aquariumFish.MutantExtraSoldReward) * weightIncEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemData.count += (int)(slotData.allCash * (1 + aquariumFish.GrowthExtraSoldReward * rewardProgress) * weightIncEvent);
|
||||
}
|
||||
}
|
||||
itemData.id = 1002;
|
||||
|
||||
itemDatas.Add(itemData);
|
||||
slotData.cash = itemData.count;
|
||||
|
||||
//重量
|
||||
Item item = _tables.TbItem.GetOrDefault(slotData.fishItemID);
|
||||
fishData = _tables.TbFishData.GetOrDefault(item.RedirectID);
|
||||
//float weightCount = fishData.BaseWeight * (1 + aquariumFish.GrowthExtraSoldReward * rewardProgress) * weightIncEvent;
|
||||
//playerFishData.SetWeight(fishData.ID, weightCount, false);
|
||||
#if AGG
|
||||
int cardLevel = playerFishData.GetDataLevel(fishData.ID);
|
||||
using (var e = GEvent.GameEvent("aquarium_sell"))
|
||||
{
|
||||
e.AddContent("map_id", data.mapId)
|
||||
.AddContent("total_eggpots", data.slot)
|
||||
.AddContent("fish_eggs_quality", slotData.eggId)
|
||||
.AddContent("mature_percent", rewardProgress)
|
||||
.AddContent("fish_id", fishData.ID)
|
||||
.AddContent("fishcard_level", cardLevel)
|
||||
.AddContent("fish_quality", slotData.aquariumFishID)
|
||||
.AddContent("mutant_count", slotData.mutants)
|
||||
.AddContent("is_injured", is_injured)
|
||||
.AddContent("fish_count", slotData.fishMQ)
|
||||
.AddContent("is_dead", false)
|
||||
.AddContent("reward_cash", slotData.cash);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
//GContext.Publish(new ShowData(itemDatas));
|
||||
GContext.container.Resolve<PlayerItemData>().AddItem(itemDatas, null);
|
||||
//GContext.Publish(new ShowData());
|
||||
CheckInHome();
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public float SetRewardCount(int dropID)
|
||||
{
|
||||
DropPackageList dropPackageList = _tables.TbDrop[dropID].DropList;
|
||||
return dropPackageList.DropCountList[0];
|
||||
}
|
||||
|
||||
public void CheckInHome()
|
||||
{
|
||||
data.isHome = true;
|
||||
List<AquariumResultData> aquariumResultDatas = data.aquariumResultDatas;
|
||||
var slotDatas = data.slotDatas;
|
||||
for (int i = 0; i < data.slot; i++)
|
||||
{
|
||||
if (!slotDatas[i].IsResult() && !aquariumResultDatas[i].IsResult())
|
||||
{
|
||||
data.isHome = false;
|
||||
}
|
||||
}
|
||||
if (data.isHome)
|
||||
{
|
||||
#if AGG
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
for (int i = 0; i < data.slot; i++)
|
||||
{
|
||||
if (!slotDatas[i].IsResult() && aquariumResultDatas[i].IsResult())
|
||||
{
|
||||
var fishItem = _tables.TbItem.GetOrDefault(slotDatas[i].fishItemID);
|
||||
int cardLevel = playerFishData.GetDataLevel(fishItem.RedirectID);
|
||||
using (var e = GEvent.GameEvent("aquarium_sell"))
|
||||
{
|
||||
e.AddContent("map_id", data.mapId)
|
||||
.AddContent("total_eggpots", data.slot)
|
||||
.AddContent("fish_eggs_quality", slotDatas[i].eggId)
|
||||
.AddContent("mature_percent", 0)
|
||||
.AddContent("fish_id", fishItem.RedirectID)
|
||||
.AddContent("fishcard_level", cardLevel)
|
||||
.AddContent("fish_quality", slotDatas[i].aquariumFishID)
|
||||
.AddContent("mutant_count", slotDatas[i].mutants)
|
||||
.AddContent("is_injured", true)
|
||||
.AddContent("is_dead", true)
|
||||
.AddContent("fish_count", 0)
|
||||
.AddContent("reward_cash", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
UpdateUserData();
|
||||
//_ = UIManager.Instance.ShowUI(UITypes.AquariumReportPopupPanel);
|
||||
}
|
||||
}
|
||||
void UpdateUserData()
|
||||
{
|
||||
saveStore.Write(AquariumEventDataNewKey, data.Serialize()).RequireAccepted();
|
||||
}
|
||||
void SaveData(bool all = false)
|
||||
{
|
||||
List<AquariumSlotData> slotDatas = data.slotDatas;
|
||||
//鱼卡等级
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
List<int> cardLv = new List<int>();
|
||||
for (int i = 0; i < slotDatas.Count; i++)
|
||||
{
|
||||
var fishItem = _tables.TbItem.GetOrDefault(slotDatas[i].fishItemID);
|
||||
if (fishItem.Quality < 6)
|
||||
{
|
||||
int level = playerFishData.GetDataLevel(fishItem.RedirectID);
|
||||
cardLv.Add(level);
|
||||
}
|
||||
else
|
||||
{
|
||||
int level = GContext.container.Resolve<PlayerData>().benefitsLevel[4];
|
||||
cardLv.Add(level);
|
||||
}
|
||||
}
|
||||
|
||||
if (all)
|
||||
{
|
||||
UpdateAquariumAllDataRequest request = new UpdateAquariumAllDataRequest();
|
||||
request.playFabId = loginUser.UserId;
|
||||
request.data = new AquariumServerData()
|
||||
{
|
||||
startTime = data.startTime,
|
||||
mapId = data.mapId,
|
||||
slot = data.slot,
|
||||
slotDatas = data.slotDatas,
|
||||
cardLv = string.Join(',', cardLv),
|
||||
aquariumResultDatas = data.aquariumResultDatas,
|
||||
aquariumHurtDatas = data.aquariumHurtDatas,
|
||||
};
|
||||
customServerMgr.CustomServerPost("aquarium/UpdateAquariumData", Newtonsoft.Json.JsonConvert.SerializeObject(request));
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateSlotDatasRequest request = new UpdateSlotDatasRequest();
|
||||
request.playFabId = loginUser.UserId;
|
||||
request.slotDatas = data.slotDatas;
|
||||
request.cardLv = string.Join(',', cardLv);
|
||||
customServerMgr.CustomServerPost("aquarium/UpdateSlotData", Newtonsoft.Json.JsonConvert.SerializeObject(request));
|
||||
}
|
||||
UpdateUserData();
|
||||
}
|
||||
|
||||
public List<float> GetWeightMag()
|
||||
{
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
List<float> weight = new List<float>() { 0, 0, 0, 0, 0, 0 };
|
||||
List<int> fishCount = new List<int>() { 0, 0, 0, 0, 0, 0 };
|
||||
MapData mapData = _tables.TbMapData.GetOrDefault(data.mapId);
|
||||
var fishList = mapData.FishList;
|
||||
for (int j = 0; j < fishList.Count; j++)
|
||||
{
|
||||
var data = _tables.TbFishData.GetOrDefault(fishList[j]);
|
||||
fishCount[data.Quality - 1]++;
|
||||
int level = playerFishData.GetDataLevel(fishList[j]);
|
||||
if (level > 0)
|
||||
{
|
||||
FishCard fishCard = _tables.TbFishCard.GetOrDefault(data.FishCardID);
|
||||
weight[data.Quality - 1] += fishCard.WeightMagList[level - 1];
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < weight.Count; i++)
|
||||
{
|
||||
weight[i] /= fishCount[i];
|
||||
}
|
||||
weight[5] = playerData.benefitsEventFishCashMag;
|
||||
|
||||
return weight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,790 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
public class CampDataMM
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
private cfg.Tables _tables;
|
||||
MMTService _MMService;
|
||||
public CampDataMM(cfg.Tables tables, MMTService mmService)
|
||||
{
|
||||
this._tables = tables;
|
||||
_MMService = mmService;
|
||||
}
|
||||
|
||||
#region SERVER_DATA
|
||||
int _id = 101;
|
||||
public int PreID;
|
||||
public int Id
|
||||
{
|
||||
set { _id = value; }
|
||||
get
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
}
|
||||
public int AreaIndex { get; private set; } = 0;
|
||||
public int AreaStep { get; private set; } = 0;
|
||||
Dictionary<string, int> CampDataDic = new Dictionary<string, int>();
|
||||
#endregion
|
||||
|
||||
public int AreaId { get; private set; } = 0;
|
||||
public int AreaStepCount { get; private set; }
|
||||
public int TimelineId { get; private set; }
|
||||
public string Name { get { return GetMMTString(_tables.TbConstruction.DataMap[Id].Name_l10n_key); } }
|
||||
|
||||
public string AreaIcon { get; private set; }
|
||||
public string BasePrefab => GetBasePrefab(Id);
|
||||
public List<string> AllPrefabs
|
||||
{
|
||||
get
|
||||
{
|
||||
List<int> AreaList = _tables.TbConstruction.DataMap[Id].AreaList;
|
||||
return AreaList.Select(stepid => GetArea(stepid).BuildingPrefeb).Distinct().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string CurrentPrefab
|
||||
{
|
||||
get
|
||||
{
|
||||
int index = AreaIndex;
|
||||
if (index >= AreaList.Count)
|
||||
index = AreaList.Count - 1;
|
||||
|
||||
return GetArea(AreaList[index]).BuildingPrefeb;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Area GetAreaByIndex(int index)
|
||||
{
|
||||
if (index >= AreaList.Count)
|
||||
index = AreaList.Count - 1;
|
||||
return GetArea(AreaList[index]);
|
||||
}
|
||||
|
||||
|
||||
public string BGM { get { return _tables.TbConstruction.DataMap[Id].BGM; } }
|
||||
public int NextMapID { get { return _tables.TbConstruction.DataMap[Id].NextID; } }
|
||||
public Area constructionArea { get; private set; }
|
||||
public Step StepInfo { get; private set; }
|
||||
public List<int> AreaList
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tables.TbConstruction.DataMap[Id].AreaList;
|
||||
}
|
||||
}
|
||||
|
||||
public List<int> AreaStepList { get; private set; }
|
||||
|
||||
public bool IsUpdataArea = false;
|
||||
string _isOpenSkyscraper = "0";
|
||||
public bool IsOpenSkyscraper => _isOpenSkyscraper == "1" || IsCanSkyscraper;
|
||||
public bool IsCanSkyscraper
|
||||
{
|
||||
get
|
||||
{
|
||||
var dataMap = _tables.TbMapData.DataList[^1];
|
||||
return GContext.container.Resolve<PlayerData>().lv >= dataMap.LevelRequired;
|
||||
}
|
||||
}
|
||||
//public int curQRewardCount = 0;
|
||||
//是否需要修复
|
||||
public bool IsFix => _isFix && AreaIndex < AreaList.Count;
|
||||
bool _isFix;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVCampState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) => game.PlayerDataStorage.PlayerKVSaveSchema.DecodeCamp(raw,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
CampDataDic=new Dictionary<string,int>(StringComparer.Ordinal);
|
||||
_isFix=false; _isOpenSkyscraper="0"; _id=101;
|
||||
PreID=AreaIndex=AreaStep=AreaId=AreaStepCount=TimelineId=0;
|
||||
AreaIcon=null; constructionArea=null; StepInfo=null; AreaStepList=null; IsUpdataArea=false;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVCampState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
CampDataDic=state.Values.ToDictionary(p=>p.Key,p=>p.Value,StringComparer.Ordinal);
|
||||
_isFix=state.IsFix; _isOpenSkyscraper=state.Skyscraper;
|
||||
}
|
||||
internal game.PlayerDataStorage.PlayerKVCampState SnapshotForKVValidation() =>
|
||||
new game.PlayerDataStorage.PlayerKVCampState(_isFix,_isOpenSkyscraper,CampDataDic);
|
||||
public void InitData(Dictionary<string, string> userData)
|
||||
{
|
||||
string value;
|
||||
|
||||
if (userData.TryGetValue("IsFix", out value))
|
||||
{
|
||||
SetIsFix(bool.Parse(value), false);
|
||||
}
|
||||
if (userData.TryGetValue("IsOpenSkyscraper", out value))
|
||||
{
|
||||
_isOpenSkyscraper = value;
|
||||
}
|
||||
|
||||
if (userData.TryGetValue("CampDataDic", out value))
|
||||
{
|
||||
CampDataDic = JsonMapper.ToObject<Dictionary<string, int>>(value);
|
||||
}
|
||||
}
|
||||
public void SetIsFix(bool isFix, bool isSave = true)
|
||||
{
|
||||
_isFix = isFix;
|
||||
if (isSave)
|
||||
{
|
||||
SaveStore.Write("IsFix", _isFix.ToString()).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCampAreaIndex(string id_string)
|
||||
{
|
||||
AreaIndex = GlobalUtils.TryParseInt(id_string);
|
||||
}
|
||||
|
||||
public void SetCampAreaStep(string step_string)
|
||||
{
|
||||
AreaStep = GlobalUtils.TryParseInt(step_string);
|
||||
}
|
||||
void SetCampByLevel()
|
||||
{
|
||||
List<Construction> _dataList = _tables.TbConstruction.DataList;
|
||||
if (!_tables.TbConstruction.DataMap.ContainsKey(Id))
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: Not found data of id({Id})!");
|
||||
Id = _dataList[^1].ID;
|
||||
}
|
||||
int level = GContext.container.Resolve<PlayerData>().lv;
|
||||
int id = _dataList[0].ID;
|
||||
for (int i = _dataList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_dataList[i].InitialLevel <= level)
|
||||
{
|
||||
id = _dataList[i].ID;
|
||||
level -= _dataList[i].InitialLevel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AreaIndex = AreaList.Count;
|
||||
var _area = GetArea(AreaList[^1]);
|
||||
int StepCount = _area.StepCount;
|
||||
AreaStep = StepCount;
|
||||
if (id % 1000 <= Id % 1000)
|
||||
{
|
||||
Id = id;
|
||||
AreaIndex = AreaList.Count;
|
||||
for (int i = 0; i < AreaList.Count; i++)
|
||||
{
|
||||
_area = GetArea(AreaList[i]);
|
||||
StepCount = _area.StepCount;
|
||||
if (StepCount > level)
|
||||
{
|
||||
AreaIndex = i;
|
||||
AreaStep = level;
|
||||
break;
|
||||
}
|
||||
level -= StepCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Init()
|
||||
{
|
||||
if (CampDataDic.Count > 0)
|
||||
{
|
||||
Id = CampDataDic["CampId"];
|
||||
}
|
||||
SetCampByLevel();
|
||||
PreID = Id;
|
||||
if (_tables.TbConstruction.DataMap.ContainsKey(Id))
|
||||
{
|
||||
if (AreaIndex >= AreaList.Count)
|
||||
{
|
||||
CompleteInit();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCurrentData();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: Not found data of id({Id})!");
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeNextCampID()
|
||||
{
|
||||
OnChangeCamp(NextMapID);
|
||||
}
|
||||
public void ChangeCampId(int id)
|
||||
{
|
||||
OnChangeCamp(id);
|
||||
}
|
||||
|
||||
void OnChangeCamp(int id)
|
||||
{
|
||||
if (_tables.TbConstruction.DataMap.TryGetValue(id, out var camp_data))
|
||||
{
|
||||
constructionArea = null;
|
||||
Id = id;
|
||||
PreID = Id;
|
||||
SetCampByLevel();
|
||||
if (AreaIndex >= AreaList.Count)
|
||||
{
|
||||
CompleteInit();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCurrentData();
|
||||
}
|
||||
CampDataDic["CampId"] = Id;
|
||||
SaveStore.Write("CampDataDic", JsonMapper.ToJson(CampDataDic)).RequireAccepted(); ;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: Not found data of id({id})!");
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowReward()
|
||||
{
|
||||
int lv = GContext.container.Resolve<PlayerData>().lv;
|
||||
if (_tables.TbGlobalConfig.AutoClaimUnlock > 0 && lv > _tables.TbGlobalConfig.AutoClaimUnlock)
|
||||
GContext.Publish(new ShowData(RewardType.AutoOpen));
|
||||
else
|
||||
GContext.Publish(new ShowData());
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void UpdateCurrentData()
|
||||
{
|
||||
if (AreaList != null)
|
||||
{
|
||||
if (AreaIndex < 0 || AreaIndex >= AreaList.Count)
|
||||
{
|
||||
if (NextMapID == -1 || !_tables.TbConstruction.DataMap.ContainsKey(NextMapID))
|
||||
{
|
||||
_isOpenSkyscraper = "1";
|
||||
SaveStore.Write("IsOpenSkyscraper", _isOpenSkyscraper).RequireAccepted();
|
||||
return;
|
||||
}
|
||||
Debug.LogError(
|
||||
$"[CampData]UpdateCurrentData: AreaIndex({AreaIndex}) is out of range[0-{AreaList.Count}]! Will reset to 0");
|
||||
AreaIndex = 0;
|
||||
}
|
||||
|
||||
IsUpdataArea = AreaId != AreaList[AreaIndex];
|
||||
if (IsUpdataArea)
|
||||
{
|
||||
AreaId = AreaList[AreaIndex];
|
||||
SetArea();
|
||||
}
|
||||
|
||||
if (AreaStepList != null)
|
||||
{
|
||||
if (AreaStep < 0 || AreaStep >= AreaStepList.Count)
|
||||
{
|
||||
AreaStep = 0;
|
||||
Debug.LogError(
|
||||
$"[CampData]UpdateCurrentData: AreaStep({AreaStep}) is out of range[0-{AreaStepList.Count}]! Reset it to 0.");
|
||||
}
|
||||
|
||||
var step_id = AreaStepList[AreaStep];
|
||||
SetAreaStep(step_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: AreaStepList is empty!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: AreaList is empty!");
|
||||
}
|
||||
}
|
||||
|
||||
void SetArea()
|
||||
{
|
||||
constructionArea = GetArea(AreaId);
|
||||
if (constructionArea == null)
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: Not found data of area id({AreaId})!");
|
||||
}
|
||||
AreaIcon = constructionArea.Icon;
|
||||
TimelineId = constructionArea.TimelineID;
|
||||
|
||||
AreaStepCount = constructionArea.StepCount;
|
||||
AreaStepList = constructionArea.StepList;
|
||||
}
|
||||
void SetAreaStep(int step_id)
|
||||
{
|
||||
StepInfo = GetStep(step_id);
|
||||
|
||||
if (StepInfo == null)
|
||||
{
|
||||
Debug.LogError($"[CampData]UpdateCurrentData: Not found step data of id({step_id})!");
|
||||
}
|
||||
}
|
||||
void CompleteInit()
|
||||
{
|
||||
if (AreaList != null)
|
||||
{
|
||||
int curAreaIndex = AreaList.Count - 1;
|
||||
AreaId = AreaList[curAreaIndex];
|
||||
|
||||
SetArea();
|
||||
|
||||
if (AreaStepList != null)
|
||||
{
|
||||
var step_id = AreaStepList[^1];
|
||||
SetAreaStep(step_id);
|
||||
}
|
||||
if (NextMapID == -1 || !_tables.TbConstruction.DataMap.ContainsKey(NextMapID))
|
||||
{
|
||||
_isOpenSkyscraper = "1";
|
||||
SaveStore.Write("IsOpenSkyscraper", _isOpenSkyscraper).RequireAccepted();
|
||||
}
|
||||
}
|
||||
}
|
||||
void SetHome_Skyscraper()
|
||||
{
|
||||
if (IsOpenSkyscraper)
|
||||
{
|
||||
if (IsCanSkyscraper)
|
||||
{
|
||||
ulong curGold = GContext.container.Resolve<PlayerData>().gold;
|
||||
ulong basePrice =
|
||||
(ulong)GContext.container.Resolve<Tables>().TbMapData.DataList[^1].ConstrutionCost;
|
||||
var multiplierTable = GContext.container.Resolve<Tables>().TbConstructionInfiniteConfig.CashMagList;
|
||||
var ibData = new InfiniteBuildingData();
|
||||
ibData.Deserialize(GContext.container.Resolve<PlayerData>().InfiniteBuildingData);
|
||||
int step = ibData.Step;
|
||||
ulong price = (ulong)(basePrice * multiplierTable[step]);
|
||||
int count = 0;
|
||||
while (curGold >= price)
|
||||
{
|
||||
curGold -= price;
|
||||
step++;
|
||||
step %= multiplierTable.Count;
|
||||
price = (ulong)(basePrice * multiplierTable[step]);
|
||||
count++;
|
||||
if (count >= 99)
|
||||
break;
|
||||
}
|
||||
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Skyscraper, count > 0, (int)count);
|
||||
}
|
||||
else
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Skyscraper, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool GoBuilding()
|
||||
{
|
||||
if (AreaStep >= AreaStepList.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
long gold = (long)(ulong)GContext.container.Resolve<PlayerData>().gold;
|
||||
var buffTimeData = GContext.container.Resolve<BuffDataCenter>().GetWeelyBuffTimeData<ConstructionCost>();
|
||||
bool isConstructionCost = buffTimeData != null;
|
||||
long _upgradeCost;
|
||||
var step_id = AreaStepList[AreaStep];
|
||||
var camp_data = _tables.TbConstruction.DataMap[Id];
|
||||
List<int> _areaStepList = AreaStepList;
|
||||
int start;
|
||||
int endIndex;
|
||||
List<int> _areaList;
|
||||
_areaList = camp_data.AreaList;
|
||||
start = _tables.TbConstructionStep.DataList.IndexOf(_tables.TbConstructionStep.DataMap[step_id]);
|
||||
_areaStepList = GetArea(_areaList[^1]).StepList;
|
||||
endIndex = _tables.TbConstructionStep.DataList.IndexOf(_tables.TbConstructionStep.DataMap[_areaStepList[^1]]);
|
||||
List<ConstructionStep> DataList = _tables.TbConstructionStep.DataList;
|
||||
for (int i = start; i <= endIndex; i++)
|
||||
{
|
||||
if (isConstructionCost)
|
||||
{
|
||||
_upgradeCost = (long)(DataList[i].BuildRequired * (1 - buffTimeData.Param));
|
||||
}
|
||||
else
|
||||
{
|
||||
_upgradeCost = DataList[i].BuildRequired;
|
||||
}
|
||||
gold -= _upgradeCost;
|
||||
if (gold < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public bool GoBuilding02(int endId)
|
||||
{
|
||||
if (AreaStep >= AreaStepList.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var step_id = AreaStepList[AreaStep];
|
||||
if (step_id < 101003 || step_id >= endId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
long gold = (long)(ulong)GContext.container.Resolve<PlayerData>().gold;
|
||||
long _upgradeCost;
|
||||
|
||||
int start = _tables.TbConstructionStep.DataList.IndexOf(_tables.TbConstructionStep.DataMap[step_id]);
|
||||
int endIndex = _tables.TbConstructionStep.DataList.IndexOf(_tables.TbConstructionStep.DataMap[endId]);
|
||||
List<ConstructionStep> DataList = _tables.TbConstructionStep.DataList;
|
||||
for (int i = start; i < endIndex; i++)
|
||||
{
|
||||
_upgradeCost = DataList[i].BuildRequired;
|
||||
gold -= _upgradeCost;
|
||||
if (gold < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从当前 <see cref="AreaStep"/> 起,用给定金币连续扣款,返回能支付多少步;含 ConstructionCost buff,与建造红点累计逻辑一致。全程 <see cref="ulong"/>,避免金币超出 <c>long.MaxValue</c> 时强转失真。
|
||||
/// </summary>
|
||||
int CountAffordableConstructionSteps(ulong gold)
|
||||
{
|
||||
if (AreaStepList == null || AreaStep >= AreaStepList.Count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var step_id = AreaStepList[AreaStep];
|
||||
int nextId = NextMapID;
|
||||
var camp_data = _tables.TbConstruction.DataMap[Id];
|
||||
while (_tables.TbConstruction.DataMap.TryGetValue(nextId, out var _camp_data))
|
||||
{
|
||||
camp_data = _camp_data;
|
||||
nextId = camp_data.NextID;
|
||||
}
|
||||
|
||||
var buffTimeData = GContext.container.Resolve<BuffDataCenter>().GetWeelyBuffTimeData<ConstructionCost>();
|
||||
bool isConstructionCost = buffTimeData != null;
|
||||
|
||||
List<int> _areaList = camp_data.AreaList;
|
||||
int start = _tables.TbConstructionStep.DataList.IndexOf(_tables.TbConstructionStep.DataMap[step_id]);
|
||||
List<int> _areaStepList = GetArea(_areaList[^1]).StepList;
|
||||
int endIndex = _tables.TbConstructionStep.DataList.IndexOf(_tables.TbConstructionStep.DataMap[_areaStepList[^1]]);
|
||||
int length = Math.Min(start + 100, endIndex + 1);
|
||||
List<ConstructionStep> DataList = _tables.TbConstructionStep.DataList;
|
||||
|
||||
int count = 0;
|
||||
for (int i = start; i < length; i++)
|
||||
{
|
||||
ulong stepCost = isConstructionCost
|
||||
? (ulong)(DataList[i].BuildRequired * (1 - buffTimeData.Param))
|
||||
: (ulong)DataList[i].BuildRequired;
|
||||
if (gold < stepCost)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
gold -= stepCost;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public void SetRedPoint()
|
||||
{
|
||||
SetHome_Skyscraper();
|
||||
int count = 0;
|
||||
bool redPoint = false;
|
||||
var playerData = GContext.container.Resolve<PlayerData>();
|
||||
|
||||
if (GContext.container.Resolve<GuideDataCenter>().GetIsFinish(_tables.TbGlobalConfig.BuildRedPointUnlock)
|
||||
&& playerData.gold >= (ulong)StepInfo.BuildRequired)
|
||||
{
|
||||
if (AreaStepList == null || AreaStep >= AreaStepList.Count)
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Build, redPoint, count);
|
||||
return;
|
||||
}
|
||||
|
||||
count = CountAffordableConstructionSteps(playerData.gold);
|
||||
redPoint = true;
|
||||
}
|
||||
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Build, redPoint, count);
|
||||
}
|
||||
|
||||
|
||||
public int GetNextStepRequired(int allStep)
|
||||
{
|
||||
int price = 0;
|
||||
if (!IsOpenSkyscraper || !IsCanSkyscraper)
|
||||
{
|
||||
if (AreaStep < AreaStepList.Count)
|
||||
{
|
||||
var step_id = AreaStepList[AreaStep];
|
||||
List<int> BuildRequired = new List<int>();
|
||||
List<ConstructionStep> DataList = _tables.TbConstructionStep.DataList;
|
||||
int start = DataList.IndexOf(_tables.TbConstructionStep.DataMap[step_id]);
|
||||
int length = start + allStep;
|
||||
|
||||
length = Math.Min(length, DataList.Count);
|
||||
for (int i = start; i < length; i++)
|
||||
{
|
||||
allStep--;
|
||||
price += DataList[i].BuildRequired;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allStep > 0)
|
||||
{
|
||||
int basePrice = GContext.container.Resolve<Tables>().TbMapData.DataList[^1].ConstrutionCost;
|
||||
var multiplierTable = GContext.container.Resolve<Tables>().TbConstructionInfiniteConfig.CashMagList;
|
||||
var ibData = new InfiniteBuildingData();
|
||||
ibData.Deserialize(GContext.container.Resolve<PlayerData>().InfiniteBuildingData);
|
||||
int step = ibData.Step;
|
||||
for (int i = 0; i < allStep; i++)
|
||||
{
|
||||
price += (int)(basePrice * multiplierTable[step]);
|
||||
step++;
|
||||
step %= multiplierTable.Count;
|
||||
}
|
||||
}
|
||||
return price;
|
||||
}
|
||||
public int GetStepRequired(int allStep)
|
||||
{
|
||||
int price = 0;
|
||||
var start_id = AreaStepList[AreaStep];
|
||||
var lastAreaStepList = GetArea(_tables.TbConstruction.DataMap[Id].AreaList[^1]).StepList;
|
||||
var end_id = lastAreaStepList[^1];
|
||||
if (end_id - start_id < allStep)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
List<ConstructionStep> DataList = _tables.TbConstructionStep.DataList;
|
||||
int start = DataList.IndexOf(_tables.TbConstructionStep.DataMap[start_id]);
|
||||
int length = start + allStep;
|
||||
for (int i = start; i < length; i++)
|
||||
{
|
||||
price += DataList[i].BuildRequired;
|
||||
}
|
||||
return price;
|
||||
}
|
||||
|
||||
public void NextStep()
|
||||
{
|
||||
if (AreaStepList != null)
|
||||
{
|
||||
AreaStep++;
|
||||
|
||||
if (AreaStep < AreaStepList.Count)
|
||||
{
|
||||
UpdateCurrentData();
|
||||
}
|
||||
else
|
||||
{
|
||||
AreaIndex++;
|
||||
if (AreaIndex < AreaList.Count)
|
||||
{
|
||||
AreaStep = 0;
|
||||
UpdateCurrentData();
|
||||
}
|
||||
else
|
||||
{
|
||||
AreaIndex = AreaList.Count;
|
||||
AreaStep = AreaStepList.Count;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GameDebug.LogError($"[CampData]NextStep: AreaStepList is empty!");
|
||||
}
|
||||
}
|
||||
public Area GetArea(int id)
|
||||
{
|
||||
// if (id < 102000)
|
||||
// {
|
||||
// if (_MMService.MMBuilding)
|
||||
// {
|
||||
// ConstructionMMTArea areaMM = _tables.TbConstructionMMTArea.GetOrDefault(id);
|
||||
// if (areaMM != null)
|
||||
// {
|
||||
// return new Area(areaMM);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
ConstructionArea area = _tables.TbConstructionArea.GetOrDefault(id);
|
||||
if (area != null)
|
||||
return new Area(area);
|
||||
return null;
|
||||
}
|
||||
public Step GetStep(int id)
|
||||
{
|
||||
// if (id < 102000)
|
||||
// {
|
||||
// if (_MMService.MMBuilding)
|
||||
// {
|
||||
// ConstructionMMTStep stepMM = _tables.TbConstructionMMTStep.GetOrDefault(id);
|
||||
// if (stepMM != null)
|
||||
// {
|
||||
// return new Step(stepMM);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
ConstructionStep step = _tables.TbConstructionStep.GetOrDefault(id);
|
||||
if (step != null)
|
||||
return new Step(step);
|
||||
return null;
|
||||
}
|
||||
public string GetBasePrefab(int id)
|
||||
{
|
||||
List<int> AreaList = _tables.TbConstruction.DataMap[id].AreaList;
|
||||
return GetArea(AreaList[0]).BuildingPrefeb;
|
||||
}
|
||||
|
||||
public string GetMMTString(string oldValue)
|
||||
{
|
||||
// if (_MMService.MMBuilding)
|
||||
// {
|
||||
// var mmt = _tables.TbMMTStringData.GetOrDefault(oldValue);
|
||||
// if (mmt != null)
|
||||
// {
|
||||
// return mmt.NewString;
|
||||
// }
|
||||
// }
|
||||
return oldValue;
|
||||
}
|
||||
}
|
||||
public class Area
|
||||
{
|
||||
public int ID { get; private set; }
|
||||
/// <summary>
|
||||
/// Building脚本中的序号,从0开始
|
||||
/// </summary>
|
||||
public int TimelineID { get; private set; }
|
||||
public int StepCount { get; private set; }
|
||||
public string BuildingPrefeb { get; private set; }
|
||||
/// <summary>
|
||||
/// 不同prefeb转换的时候,上一个timeline播放时间
|
||||
/// </summary>
|
||||
public float TimelinePlaytime { get; private set; }
|
||||
/// <summary>
|
||||
/// 多语言key + 中文
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
public string Name_l10n_key { get; }
|
||||
/// <summary>
|
||||
/// 多语言key + 中文
|
||||
/// </summary>
|
||||
public string NameIndex { get; private set; }
|
||||
public string NameIndex_l10n_key { get; }
|
||||
public string Icon { get; private set; }
|
||||
/// <summary>
|
||||
/// 对应Drop表主键
|
||||
/// </summary>
|
||||
public int Reward { get; private set; }
|
||||
public System.Collections.Generic.List<int> StepList { get; private set; }
|
||||
/// <summary>
|
||||
/// 用于室内关闭lookfar和looknear功能<br/>1 关闭
|
||||
/// </summary>
|
||||
public int TurnOffFreeLook { get; private set; }
|
||||
/// <summary>
|
||||
/// 用于室内关闭加速功能<br/>1 关闭
|
||||
/// </summary>
|
||||
public int TurnOffTimeScale { get; private set; }
|
||||
public Area(ConstructionArea area)
|
||||
{
|
||||
this.ID = area.ID;
|
||||
this.TimelineID = area.TimelineID;
|
||||
this.StepCount = area.StepCount;
|
||||
this.BuildingPrefeb = area.BuildingPrefeb;
|
||||
this.TimelinePlaytime = area.TimelinePlaytime;
|
||||
this.Name = area.Name;
|
||||
this.Name_l10n_key = area.Name_l10n_key;
|
||||
this.NameIndex = area.NameIndex;
|
||||
this.NameIndex_l10n_key = area.NameIndex_l10n_key;
|
||||
this.Icon = area.Icon;
|
||||
this.Reward = area.Reward;
|
||||
this.StepList = area.StepList;
|
||||
this.TurnOffFreeLook = area.TurnOffFreeLook;
|
||||
this.TurnOffTimeScale = area.TurnOffTimeScale;
|
||||
|
||||
}
|
||||
public Area(ConstructionMMTArea area)
|
||||
{
|
||||
this.ID = area.ID;
|
||||
this.TimelineID = area.TimelineID;
|
||||
this.StepCount = area.StepCount;
|
||||
this.BuildingPrefeb = area.BuildingPrefeb;
|
||||
this.TimelinePlaytime = area.TimelinePlaytime;
|
||||
this.Name = area.Name;
|
||||
this.Name_l10n_key = area.Name_l10n_key;
|
||||
this.NameIndex = area.NameIndex;
|
||||
this.NameIndex_l10n_key = area.NameIndex_l10n_key;
|
||||
this.Icon = area.Icon;
|
||||
this.Reward = area.Reward;
|
||||
this.StepList = area.StepList;
|
||||
this.TurnOffFreeLook = area.TurnOffFreeLook;
|
||||
this.TurnOffTimeScale = area.TurnOffTimeScale;
|
||||
}
|
||||
}
|
||||
|
||||
public class Step
|
||||
{
|
||||
public int ID { get; private set; }
|
||||
/// <summary>
|
||||
/// timeline中时间
|
||||
/// </summary>
|
||||
public float StartTime { get; private set; }
|
||||
/// <summary>
|
||||
/// 动画播放时间
|
||||
/// </summary>
|
||||
public float PlayTime { get; private set; }
|
||||
/// <summary>
|
||||
/// 建造所需钞票
|
||||
/// </summary>
|
||||
public int BuildRequired { get; private set; }
|
||||
/// <summary>
|
||||
/// 修复所需钞票
|
||||
/// </summary>
|
||||
public int FixRequired { get; private set; }
|
||||
|
||||
public Step(ConstructionStep step)
|
||||
{
|
||||
this.ID = step.ID;
|
||||
this.StartTime = step.StartTime;
|
||||
this.PlayTime = step.PlayTime;
|
||||
this.BuildRequired = step.BuildRequired;
|
||||
this.FixRequired = step.FixRequired;
|
||||
}
|
||||
public Step(ConstructionMMTStep step)
|
||||
{
|
||||
this.ID = step.ID;
|
||||
this.StartTime = step.StartTime;
|
||||
this.PlayTime = step.PlayTime;
|
||||
this.BuildRequired = step.BuildRequired;
|
||||
this.FixRequired = step.FixRequired;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
using asap.core;
|
||||
using PlayFab;
|
||||
using PlayFab.ClientModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using game.PlayerDataStorage;
|
||||
|
||||
public class PlayFabMgr : MonoBehaviour
|
||||
{
|
||||
const int USER_DATA_UPDATE_INTERVAL = 40;
|
||||
const int Buffer_Count = 5;
|
||||
const string Last_Update_Time_Key_Prefix = "LastUpdateTime";
|
||||
const string Local_Data_Key_prefix = "LocalData";
|
||||
private string Last_Update_Time_Key;
|
||||
private string Local_Data_Key;
|
||||
|
||||
private LinkedList<Dictionary<string, string>> dataBuffer;
|
||||
private Dictionary<string, string> swapData;
|
||||
private Dictionary<string, long> lastUpdateTime;
|
||||
private Dictionary<string, string> localData;
|
||||
|
||||
private CancellationTokenSource tokenSource;
|
||||
|
||||
static PlayFabMgr _instance = null;
|
||||
// An inactive, never-awakened component proves that no timer or PF send has ever been started.
|
||||
internal bool IsUnstartedForKVLoad => tokenSource==null && dataBuffer==null && swapData==null && localData==null && lastUpdateTime==null && _instance!=this;
|
||||
internal static bool HasLiveInstanceForKVLoad => _instance!=null;
|
||||
|
||||
public static PlayFabMgr Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
GameDebug.LogError("PlayFabMgr Instance doesn't exist yet!");
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, string> CompareDatas(Dictionary<string, UserDataRecord> remoteDatas, string userId)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) throw PlayerKVGameplayLoop.Stop("PF archive comparison is disabled in KV loop.");
|
||||
#endif
|
||||
PlayerKVGameLoadGuard.RequireLegacyAllowed(PlayerKVGuardPoint.Compare);
|
||||
Last_Update_Time_Key = $"{Last_Update_Time_Key_Prefix}_{userId}";
|
||||
Local_Data_Key = $"{Local_Data_Key_prefix}_{userId}";
|
||||
var lastUpdateTimeStr = PlayerPrefs.GetString(Last_Update_Time_Key, string.Empty);
|
||||
var now = ZZTimeHelper.UtcNow().Ticks;
|
||||
localData = new Dictionary<string, string>();
|
||||
lastUpdateTime = new Dictionary<string, long>();
|
||||
|
||||
if (string.IsNullOrEmpty(lastUpdateTimeStr))
|
||||
{
|
||||
if (remoteDatas != null && remoteDatas.Count > 0)
|
||||
{
|
||||
lastUpdateTime = remoteDatas.ToDictionary(x => x.Key, x => x.Value.LastUpdated.Ticks);
|
||||
localData = remoteDatas.ToDictionary(x => x.Key, x => x.Value.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var tempLastUpdateTime = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, long>>(lastUpdateTimeStr);
|
||||
var localDataStr = PlayerPrefs.GetString(Local_Data_Key);
|
||||
var tempLocalData = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(localDataStr);
|
||||
var allKeys = tempLocalData.Keys.Concat(remoteDatas.Keys).Distinct();
|
||||
foreach (var key in allKeys)
|
||||
{
|
||||
var localValue = tempLocalData.ContainsKey(key) ? tempLocalData[key] : null;
|
||||
var remoteValue = remoteDatas.ContainsKey(key) ? remoteDatas[key].Value : null;
|
||||
var localTime = tempLastUpdateTime.ContainsKey(key) ? tempLastUpdateTime[key] : 0;
|
||||
var remoteTime = remoteDatas.ContainsKey(key) ? remoteDatas[key].LastUpdated.Ticks : 0;
|
||||
|
||||
if (localValue == null)
|
||||
{
|
||||
localData[key] = remoteValue;
|
||||
lastUpdateTime[key] = remoteTime;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (remoteValue == null)
|
||||
{
|
||||
UpdateUserDataValue(key, localValue);
|
||||
lastUpdateTime[key] = now;
|
||||
localData[key] = localValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
var timeDelta = localTime - remoteTime;
|
||||
if (Math.Abs(timeDelta) > TimeSpan.TicksPerSecond * 60)
|
||||
{
|
||||
if (localTime > remoteTime)
|
||||
{
|
||||
UpdateUserDataValue(key, localValue);
|
||||
lastUpdateTime[key] = now;
|
||||
localData[key] = localValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastUpdateTime[key] = remoteTime;
|
||||
localData[key] = remoteValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lastUpdateTime[key] = localTime;
|
||||
localData[key] = localValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlayerPrefs.SetString(Last_Update_Time_Key, LitJson.JsonMapper.ToJson(lastUpdateTime));
|
||||
PlayerPrefs.SetString(Local_Data_Key, LitJson.JsonMapper.ToJson(localData));
|
||||
return localData;
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
_instance = this;
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) return;
|
||||
#endif
|
||||
InitBuffer();
|
||||
tokenSource = new CancellationTokenSource();
|
||||
UpdateUserDataValue(tokenSource.Token);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
internal void StartKVGameplayLoopSync()
|
||||
{
|
||||
if(!PlayerKVGameplayLoop.Ready || tokenSource!=null) return;
|
||||
tokenSource=new CancellationTokenSource();
|
||||
UpdateUserDataValue(tokenSource.Token);
|
||||
}
|
||||
internal void StopKVGameplayLoopSync()
|
||||
{ tokenSource?.Cancel(); tokenSource?.Dispose(); tokenSource=null; }
|
||||
#endif
|
||||
void InitBuffer()
|
||||
{
|
||||
dataBuffer = new LinkedList<Dictionary<string, string>>();
|
||||
for (int i = 0; i < Buffer_Count; i++)
|
||||
{
|
||||
dataBuffer.AddLast(new Dictionary<string, string>());
|
||||
}
|
||||
swapData = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
void OnApplicationPause(bool is_pause)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled)
|
||||
{
|
||||
if(is_pause) { StopKVGameplayLoopSync(); _=PlayerKVGameplayLoop.FlushObservedAsync(); }
|
||||
else StartKVGameplayLoopSync();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
if (is_pause)
|
||||
{
|
||||
tokenSource.Cancel();
|
||||
tokenSource = null;
|
||||
SyncData();
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenSource = new CancellationTokenSource();
|
||||
UpdateUserDataValue(tokenSource.Token);
|
||||
}
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) { StopKVGameplayLoopSync(); _=PlayerKVGameplayLoop.CloseAsync(); return; }
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
tokenSource.Cancel();
|
||||
tokenSource = null;
|
||||
SyncData();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) { StopKVGameplayLoopSync(); _=PlayerKVGameplayLoop.CloseAsync(); return; }
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
tokenSource?.Cancel();
|
||||
tokenSource = null;
|
||||
if(!PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.PrefsSave)) PlayerPrefs.Save();
|
||||
SyncData();
|
||||
}
|
||||
|
||||
private async void UpdateUserDataValue(CancellationToken token)
|
||||
{
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Lifecycle)) return;
|
||||
try
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
SyncData();
|
||||
await Task.Delay(USER_DATA_UPDATE_INTERVAL * 1000);
|
||||
}
|
||||
}
|
||||
catch (System.Exception) { }
|
||||
}
|
||||
|
||||
public void UpdateUserDataValue(string key, string data, bool remoteStore = true)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) { PlayerKVGameplayLoop.Write(key,data,remoteStore); return; }
|
||||
#endif
|
||||
PlayerKVGameLoadGuard.RequireLegacyAllowed(PlayerKVGuardPoint.Update);
|
||||
if (remoteStore)
|
||||
{
|
||||
var cbuff = dataBuffer.First;
|
||||
while (cbuff != null)
|
||||
{
|
||||
var datas = cbuff.Value;
|
||||
if (!datas.ContainsKey(key) && datas.Count >= 10)
|
||||
{
|
||||
cbuff = cbuff.Next;
|
||||
continue;
|
||||
}
|
||||
cbuff.Value[key] = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
localData[key] = data;
|
||||
lastUpdateTime[key] = ZZTimeHelper.UtcNow().Ticks;
|
||||
//Debug.Log($"[PlayFabMgr]UpdateUserDataValue key:{key} data:{data}");
|
||||
PlayerPrefs.SetString(Last_Update_Time_Key, LitJson.JsonMapper.ToJson(lastUpdateTime));
|
||||
PlayerPrefs.SetString(Local_Data_Key, LitJson.JsonMapper.ToJson(localData));
|
||||
}
|
||||
/// <summary>
|
||||
/// 没有数据返回 null 一定一定一定判空!!!
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public string GetLocalData(string key)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) return PlayerKVGameplayLoop.Read(key);
|
||||
#endif
|
||||
PlayerKVGameLoadGuard.RequireLegacyAllowed(PlayerKVGuardPoint.ReadLocal);
|
||||
if (localData.ContainsKey(key))
|
||||
{
|
||||
return localData[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncData()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) { _=PlayerKVGameplayLoop.FlushObservedAsync(); return; }
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Sync)) return;
|
||||
// Logout clears credentials before RootCtx is destroyed. Keep pending data local.
|
||||
if (!PlayFabClientAPI.IsClientLoggedIn()) return;
|
||||
var buf = dataBuffer.First;
|
||||
if (buf.Value.Count == 0)
|
||||
return;
|
||||
|
||||
var datas = buf.Value;
|
||||
swapData.Clear();
|
||||
buf.Value = swapData;
|
||||
swapData = datas;
|
||||
|
||||
dataBuffer.RemoveFirst();
|
||||
dataBuffer.AddLast(buf);
|
||||
|
||||
var request = new UpdateUserDataRequest()
|
||||
{
|
||||
Data = swapData,
|
||||
Permission = UserDataPermission.Public
|
||||
};
|
||||
|
||||
PlayFabClientAPI.UpdateUserData(request, null, UpdateFaild);
|
||||
#if UNITY_EDITOR
|
||||
//IConfig config = GContext.container.Resolve<IConfig>();
|
||||
//if (config != null && config.Get<string>("DBUG", string.Empty) == "1")
|
||||
//{
|
||||
try
|
||||
{
|
||||
//写入本地文件记录 key 和时间
|
||||
string persistentPath = Application.persistentDataPath;
|
||||
// 拼接文件名,这里使用txt文件记录数据
|
||||
string filePath = System.IO.Path.Combine(persistentPath, "syncData.csv");
|
||||
string currentTime = ZZTimeHelper.UtcNow().ToString("yyyy-MM-dd HH:mm:ss");
|
||||
string dataLine = $"{currentTime},{string.Join(',', swapData.Keys)}";
|
||||
// 使用追加模式写入文件,每次写入一行数据
|
||||
using (System.IO.StreamWriter writer = System.IO.File.AppendText(filePath))
|
||||
{
|
||||
writer.WriteLine(dataLine);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"记录数据时出错: {e.Message}");
|
||||
}
|
||||
//}
|
||||
#endif
|
||||
}
|
||||
|
||||
private void UpdateFaild(PlayFabError error)
|
||||
{
|
||||
GameDebug.LogError("[PlayFabMgr]UpdateUserData Error: {0}", error.ToString());
|
||||
}
|
||||
|
||||
internal void FlushBufferedData() => SyncData();
|
||||
|
||||
public async Task<bool> SyncDataImmidiate(string key, string data)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled) throw PlayerKVGameplayLoop.Stop("Immediate/consuming saves are outside the equipment loop.");
|
||||
#endif
|
||||
if(PlayerKVGameLoadGuard.Reject(PlayerKVGuardPoint.Immediate)) return false;
|
||||
foreach (var buff in dataBuffer)
|
||||
{
|
||||
if (buff.ContainsKey(key))
|
||||
{
|
||||
buff.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
var taskSource = new TaskCompletionSource<bool>();
|
||||
|
||||
var request = new UpdateUserDataRequest()
|
||||
{
|
||||
Data = new Dictionary<string, string>() { { key, data } },
|
||||
Permission = UserDataPermission.Public
|
||||
};
|
||||
|
||||
PlayFabClientAPI.UpdateUserData(request,
|
||||
(r) => { taskSource.SetResult(true); },
|
||||
(e) =>
|
||||
{
|
||||
taskSource.SetResult(false);
|
||||
UnityEngine.Debug.LogError($"[PlayFabMgr]UpdateUserData Error: {e.ToString()}");
|
||||
}
|
||||
);
|
||||
return await taskSource.Task;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using game;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class BargainPackData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
private readonly Tables _tables = GContext.container.Resolve<Tables>();
|
||||
private readonly TbEventPackManager _epm = GContext.container.Resolve<Tables>().TbEventPackManager;
|
||||
private readonly TbSpecialPack _spp = GContext.container.Resolve<Tables>().TbSpecialPack;
|
||||
private GeneralEventPackData _data;
|
||||
|
||||
private bool _kvLoadPresent;
|
||||
#if UNITY_EDITOR
|
||||
internal bool SelectionsAreIndependentForKVValidation()
|
||||
{
|
||||
var source = new List<int> { 7, 9 };
|
||||
var state = new game.PlayerDataStorage.PlayerKVBargainState(true,0,_data.redirectID,0,0,0,0,0,0,0,0,0,false,false,0,source);
|
||||
ReplaceFromKVDecoded(state);
|
||||
source.Clear();
|
||||
if (_data.selections.Count != 2 || ReferenceEquals(_data.selections, source) || ReferenceEquals(_data.selections, state.selections)) return false;
|
||||
var previous = _data.selections;
|
||||
_data.selections[0] = 10;
|
||||
if (state.selections[0] != 7) return false;
|
||||
ResetForKVLoad();
|
||||
return _data.selections.Count == 0 && !ReferenceEquals(previous, _data.selections) && !_kvLoadPresent;
|
||||
}
|
||||
#endif
|
||||
internal bool KVLoadPresent => _kvLoadPresent;
|
||||
internal static game.PlayerDataStorage.PlayerKVBargainState DecodeForKVLoad(
|
||||
IReadOnlyDictionary<string, string> raw, game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeBargain(raw, snapshot, errors);
|
||||
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
_kvLoadPresent = false;
|
||||
_data = default(GeneralEventPackData);
|
||||
_data.selections = new List<int>();
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVBargainState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
if (!state.IsPresent) { ResetForKVLoad(); return; }
|
||||
var replacement = new GeneralEventPackData
|
||||
{
|
||||
currentEventID = state.currentEventID,
|
||||
redirectID = state.redirectID,
|
||||
VIPLvlWhenEventActivate = state.VIPLvlWhenEventActivate,
|
||||
purchaseCount = state.purchaseCount,
|
||||
savingProgress = state.savingProgress,
|
||||
discountLvl = state.discountLvl,
|
||||
visualProgress = state.visualProgress,
|
||||
visualDiscountLvl = state.visualDiscountLvl,
|
||||
rodID = state.rodID,
|
||||
lastPackIDBought = state.lastPackIDBought,
|
||||
packID = state.packID,
|
||||
doNeedTrigger = state.doNeedTrigger,
|
||||
isTriggered = state.isTriggered,
|
||||
inflation = state.inflation,
|
||||
selections = state.selections == null ? null : new List<int>(state.selections),
|
||||
};
|
||||
_data = replacement;
|
||||
_kvLoadPresent = true;
|
||||
}
|
||||
internal game.PlayerDataStorage.PlayerKVBargainState SnapshotForKVValidation() =>
|
||||
new game.PlayerDataStorage.PlayerKVBargainState(_kvLoadPresent, _data.currentEventID, _data.redirectID, _data.VIPLvlWhenEventActivate, _data.purchaseCount, _data.savingProgress, _data.discountLvl, _data.visualProgress, _data.visualDiscountLvl, _data.rodID, _data.lastPackIDBought, _data.packID, _data.doNeedTrigger, _data.isTriggered, _data.inflation, _data.selections);
|
||||
|
||||
public int CurrentEventID => _data.currentEventID;
|
||||
|
||||
public FishingEvent CurrentEvent => _tables.TbFishingEvent[_data.currentEventID];
|
||||
|
||||
public TimeSpan RemainingTime =>
|
||||
DateTime.Parse((_tables.TbFishingEvent[_data.currentEventID].TimeDefinition as LimitedTime).EndTime) -
|
||||
ZZTimeHelper.UtcNow();
|
||||
|
||||
public bool IsWithinEventTime
|
||||
{
|
||||
get
|
||||
{
|
||||
var fishingEvent = _tables.TbFishingEvent.GetOrDefault(CurrentEventID);
|
||||
if (!(fishingEvent?.TimeDefinition is LimitedTime time)) return false;
|
||||
|
||||
var et = DateTime.Parse(time.EndTime);
|
||||
var st = DateTime.Parse(time.StartTime);
|
||||
return ZZTimeHelper.UtcNow() >= st && ZZTimeHelper.UtcNow() < et;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPackActivated
|
||||
{
|
||||
get
|
||||
{
|
||||
bool res = _tables.TbFishingEvent.DataMap.ContainsKey(_data.currentEventID)
|
||||
&& _epm.DataMap.ContainsKey(_data.redirectID)
|
||||
&& _epm[_data.redirectID].PackType == 3
|
||||
&& IsWithinEventTime
|
||||
&& _data.isTriggered
|
||||
&& _data.purchaseCount < _epm[_data.redirectID].MaxCount;
|
||||
//Debug.Log(_tables.TbFishingEvent.DataMap.ContainsKey(_data.currentEventID));
|
||||
//Debug.Log(_epm.DataMap.ContainsKey(_data.redirectID));
|
||||
//Debug.Log(_epm[_data.redirectID].PackType == 3);
|
||||
//Debug.Log(IsWithinEventTime);
|
||||
//Debug.Log(_data.isTriggered);
|
||||
//Debug.Log(_data.purchaseCount < _epm[_data.redirectID].MaxCount);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public int RedirectID => _data.redirectID;
|
||||
public bool DoNeedTrigger => _data.doNeedTrigger;
|
||||
public int PackID => _epm[_data.redirectID].VIPPackList[_data.VIPLvlWhenEventActivate][0];
|
||||
public int DiscountLvlCount => _spp[PackID].IAPID.Count;
|
||||
public int DiscountLvl => _data.discountLvl;
|
||||
public int PurchaseCount => _data.purchaseCount;
|
||||
public int Progress => _data.savingProgress;
|
||||
public int VIPLvlWhenEventActivated => _data.VIPLvlWhenEventActivate;
|
||||
public int ActivateItemId => _spp[PackID].ActivateItemId;
|
||||
public string ActiveItemImg => _spp[PackID].ActiveItemImg;
|
||||
public int EventItemId => IsPackActivated ? _spp[PackID].EventItemId : 0;
|
||||
public int RewardDropID => _spp[PackID].DropID[0];
|
||||
public int MaxCount => _epm[_data.redirectID].MaxCount;
|
||||
public float Inflation => _data.inflation;
|
||||
|
||||
public int NextTarget
|
||||
{
|
||||
get
|
||||
{
|
||||
int lvl = _data.discountLvl >= DiscountLvlCount - 1 ? DiscountLvlCount - 1 : _data.discountLvl + 1;
|
||||
int count = _spp[PackID].EventItemRequire[lvl];
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
count = playerItemData.IntInflation(count, _data.inflation);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFull => _data.discountLvl >= DiscountLvlCount - 1 && _data.savingProgress >= NextTarget;
|
||||
public int IAPID => _spp[PackID].IAPID[DiscountLvl];
|
||||
public List<int> FishingScoreList => _spp[PackID].EventItemGet;
|
||||
public int NextDiscount
|
||||
{
|
||||
get
|
||||
{
|
||||
int lvl = _data.discountLvl >= DiscountLvlCount - 1 ? DiscountLvlCount - 1 : _data.discountLvl + 1;
|
||||
return _spp[PackID].DiscountList[lvl];
|
||||
}
|
||||
}
|
||||
public int VisualProgress => _data.visualProgress;
|
||||
public int VisualDiscountLvl => _data.visualDiscountLvl;
|
||||
public int NextVisualTarget
|
||||
{
|
||||
get
|
||||
{
|
||||
int lvl = _data.visualDiscountLvl >= DiscountLvlCount - 1
|
||||
? DiscountLvlCount - 1
|
||||
: _data.visualDiscountLvl + 1;
|
||||
int EventItemRequire = _spp[PackID].EventItemRequire[lvl];
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
EventItemRequire = playerItemData.IntInflation(EventItemRequire, _data.inflation);
|
||||
return EventItemRequire;
|
||||
}
|
||||
}
|
||||
|
||||
public int NextVisualDiscount
|
||||
{
|
||||
get
|
||||
{
|
||||
int lvl = _data.visualDiscountLvl >= DiscountLvlCount - 1
|
||||
? DiscountLvlCount - 1
|
||||
: _data.visualDiscountLvl + 1;
|
||||
return _spp[PackID].DiscountList[lvl];
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveData()
|
||||
{
|
||||
//Debug.LogError("Testing, data not saved for debug purpose.");
|
||||
SaveStore.Write("GeneralEventPackData",
|
||||
Newtonsoft.Json.JsonConvert.SerializeObject(_data)).RequireAccepted();
|
||||
}
|
||||
|
||||
public void LoadData(GeneralEventPackData data)
|
||||
{
|
||||
_data = data;
|
||||
if (IsPackActivated && IsFull)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIForce(_data.currentEventID, UITypes.GiftBargainPopupPanel);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateData(FishingEvent e)
|
||||
{
|
||||
if (_epm[_tables.TbFishingEvent[e.ID].RedirectID].PackType != 3)
|
||||
{
|
||||
Debug.LogError($"Fishing event ID {e.ID} is not a Bargain back event");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_data.currentEventID != e.ID)
|
||||
{
|
||||
_data = new GeneralEventPackData(
|
||||
eventID: e.ID,
|
||||
vip: GContext.container.Resolve<PlayerData>().PriceLv,
|
||||
redirectID: e.RedirectID,
|
||||
savingProgress: 0,
|
||||
discountLvl: 0,
|
||||
isTriggered: false,
|
||||
doNeedTrigger: true,
|
||||
visualProgress: 0,
|
||||
visualDiscountLvl: 0,
|
||||
purchaseCount: 0,
|
||||
inflation: GContext.container.Resolve<PlayerData>().InflationRate);
|
||||
}
|
||||
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public void TriggerPack()
|
||||
{
|
||||
_data.doNeedTrigger = false;
|
||||
_data.isTriggered = true;
|
||||
int EventItemCountFirst = _spp[PackID].EventItemCountFirst;
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
EventItemCountFirst = playerItemData.IntInflation(EventItemCountFirst, _data.inflation);
|
||||
AddProgress(EventItemCountFirst);
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public void AddProgress(int progress)
|
||||
{
|
||||
if (progress <= 0 || IsFull)
|
||||
return;
|
||||
_data.savingProgress += progress;
|
||||
while (_data.savingProgress >= NextTarget)
|
||||
{
|
||||
//DoUICut = true;
|
||||
_data.savingProgress -= NextTarget;
|
||||
_data.discountLvl++;
|
||||
if (_data.discountLvl >= DiscountLvlCount - 1)
|
||||
{
|
||||
_data.savingProgress = NextTarget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SaveData();
|
||||
return;
|
||||
}
|
||||
|
||||
public void AddProgress(int fishTier, int magnification)
|
||||
{
|
||||
if (!IsPackActivated || fishTier < 1 || fishTier > 5)
|
||||
{
|
||||
//Debug.LogError($"Fish Tier {fishTier} not defined in TbSpedialPack");
|
||||
return;
|
||||
}
|
||||
|
||||
int progress = _spp[PackID].EventItemGet[fishTier - 1] * magnification;
|
||||
if (progress <= 0 || IsFull)
|
||||
return;
|
||||
|
||||
int curProgress = _data.savingProgress;
|
||||
_data.savingProgress += progress;
|
||||
GContext.Publish(new TargetAddData(EventItemId, curProgress, progress));
|
||||
while (_data.savingProgress >= NextTarget)
|
||||
{
|
||||
//DoUICut = true;
|
||||
_data.savingProgress -= NextTarget;
|
||||
_data.discountLvl++;
|
||||
if (_data.discountLvl >= DiscountLvlCount - 1)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>()
|
||||
.AddGiftFaceUIForce(CurrentEventID, UITypes.GiftBargainPopupPanel);
|
||||
_data.savingProgress = NextTarget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SaveData();
|
||||
return;
|
||||
}
|
||||
|
||||
public void AddPurchase()
|
||||
{
|
||||
_data.purchaseCount++;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public void RefreshVisualData()
|
||||
{
|
||||
_data.visualDiscountLvl = _data.discountLvl;
|
||||
_data.visualProgress = _data.savingProgress;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public int GetNextDiscountTarget(int l)
|
||||
{
|
||||
int lvl = l >= DiscountLvlCount - 1 ? DiscountLvlCount - 1 : l + 1;
|
||||
int count = _spp[PackID].EventItemRequire[lvl];
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
count = playerItemData.IntInflation(count, _data.inflation);
|
||||
return count;
|
||||
}
|
||||
|
||||
public int GetNextDiscount(int l)
|
||||
{
|
||||
int lvl = l >= DiscountLvlCount - 1 ? DiscountLvlCount - 1 : l + 1;
|
||||
return _spp[PackID].DiscountList[lvl];
|
||||
}
|
||||
}
|
||||
|
||||
public class BargainPackProgressEvent
|
||||
{
|
||||
public int type; //0: 初始化 1:增加动画
|
||||
public int addProgress;
|
||||
}
|
||||
@@ -0,0 +1,923 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using tysdk;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
public class GetBattlePassRewardEvent
|
||||
{
|
||||
public int NormalLevel;
|
||||
public int VipLevel;
|
||||
}
|
||||
|
||||
public struct GetAllBPRewards
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public struct BpGetExp
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public struct BpVipUnlock
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class BattlePassData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
public BattlePassRecord BattlePassRecord;
|
||||
public BattleTask BattleTask;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVBattlePassState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeBattlePass(raw,snapshot,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
BattlePassRecord=new BattlePassRecord();
|
||||
BattleTask=new BattleTask();
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVBattlePassState state)
|
||||
{
|
||||
if(state==null) throw new ArgumentNullException(nameof(state));
|
||||
var record=new BattlePassRecord {
|
||||
ID=state.Record.ID,Level=state.Record.Level,Exp=state.Record.Exp,BuyVipToastIndex=state.Record.BuyVipToastIndex,
|
||||
IsUnlockVip=state.Record.IsUnlockVip,NormalRewardGet=new List<int>(state.Record.Normal),VipRewardGet=new List<int>(state.Record.Vip),
|
||||
LastDropIDs=new List<int>(state.Record.LastDrops),InflationRate=state.Record.Inflation
|
||||
};
|
||||
var task=new BattleTask {
|
||||
dic_dailyTasks=state.Task.Daily.ToDictionary(p=>p.Key,p=>p.Value),dic_weeklyTasks=state.Task.Weekly.ToDictionary(p=>p.Key,p=>p.Value),
|
||||
loginDays=new List<int>(state.Task.LoginDays),DailyTaskEndTime=state.Task.DailyEnd,WeeklyTaskEndTime=state.Task.WeeklyEnd
|
||||
};
|
||||
BattlePassRecord=record; BattleTask=task;
|
||||
}
|
||||
|
||||
private Tables _tables;
|
||||
|
||||
|
||||
public BattlePassData(Tables tables)
|
||||
{
|
||||
if (BattlePassRecord == null)
|
||||
BattlePassRecord = new BattlePassRecord();
|
||||
if (BattleTask == null)
|
||||
BattleTask = new BattleTask();
|
||||
|
||||
_tables = tables;
|
||||
}
|
||||
|
||||
#region Load&Save
|
||||
public void InitData(Dictionary<string, string> userDatas = null)
|
||||
{
|
||||
if (userDatas.TryGetValue("BattlePassRecordTask", out string battlePassRecordTask))
|
||||
{
|
||||
|
||||
LoadBattlePassRecordTask(battlePassRecordTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
//旧数据兼容
|
||||
if (userDatas.TryGetValue("BattlePassRecord", out string data_value))
|
||||
{
|
||||
LoadBattlePassRecord(data_value);
|
||||
}
|
||||
if (userDatas.TryGetValue("BattleTask", out data_value))
|
||||
{
|
||||
LoadBattleTask(data_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void LoadBattlePassRecordTask(string data)
|
||||
{
|
||||
string[] values = data.Split('#');
|
||||
BattlePassRecord = new BattlePassRecord();
|
||||
BattlePassRecord.DeserializeObject(values[0]);
|
||||
|
||||
BattleTask = new BattleTask();
|
||||
BattleTask.DeserializeObject(values[1]);
|
||||
}
|
||||
public void LoadBattlePassRecord(string data)
|
||||
{
|
||||
BattlePassRecord = JsonConvert.DeserializeObject<BattlePassRecord>(data);
|
||||
}
|
||||
public void LoadBattleTask(string data)
|
||||
{
|
||||
BattleTask = JsonConvert.DeserializeObject<BattleTask>(data);
|
||||
}
|
||||
void SaveBattlePassRecordTask()
|
||||
{
|
||||
string battlePassRecord = BattlePassRecord.SerializeObject();
|
||||
string battleTask = BattleTask.SerializeObject();
|
||||
playerSaveStore.Write("BattlePassRecordTask", battlePassRecord + "#" + battleTask).RequireAccepted();
|
||||
}
|
||||
public void SaveBattlePassRecord()
|
||||
{
|
||||
GContext.container.Resolve<BattlePassDataProvider>().SetRedPoint();
|
||||
SaveBattlePassRecordTask();
|
||||
//PlayFabMgr.Instance.UpdateUserDataValue("BattlePassRecord", JsonConvert.SerializeObject(BattlePassRecord));
|
||||
}
|
||||
|
||||
public void SaveBattleTask()
|
||||
{
|
||||
GContext.container.Resolve<BattlePassDataProvider>().SetRedPoint();
|
||||
SaveBattlePassRecordTask();
|
||||
//PlayFabMgr.Instance.UpdateUserDataValue("BattleTask", JsonConvert.SerializeObject(BattleTask));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BattlePass
|
||||
|
||||
public void RefreshBattlePassRecord(FishingEvent t = null)
|
||||
{
|
||||
//没有开启的战令
|
||||
if (t == null)
|
||||
{
|
||||
GContext.container.Resolve<BattlePassDataProvider>().RecordLastBPUnGetDropIDs();
|
||||
BattlePassRecord.ID = 0;
|
||||
SaveBattlePassRecord();
|
||||
return;
|
||||
}
|
||||
if (t.ID != BattlePassRecord.ID)
|
||||
{
|
||||
GContext.container.Resolve<BattlePassDataProvider>().RecordLastBPUnGetDropIDs();
|
||||
BattlePassRecord.Init(t.ID);
|
||||
RefreshDaiyTask();
|
||||
RefreshWeeklyTask();
|
||||
SaveBattlePassRecord();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Task
|
||||
|
||||
public void RefreshDaiyTask()
|
||||
{
|
||||
var fishingEvent = _tables.TbFishingEvent.GetOrDefault(BattlePassRecord.ID);
|
||||
if (fishingEvent == null) return;
|
||||
var bpMain = _tables.TbBattlePassMain.GetOrDefault(fishingEvent.RedirectID);
|
||||
if (bpMain == null) return;
|
||||
BattleTask.DailyTaskEndTime = ConvertTools.GetDateTimeYMD(ZZTimeHelper.UtcNow().UtcNowOffset()).AddDays(1);
|
||||
var dailyTasks = bpMain.DailyTaskList;
|
||||
BattleTask.dic_dailyTasks.Clear();
|
||||
foreach (var task in dailyTasks)
|
||||
{
|
||||
BattleTask.dic_dailyTasks.Add(task, 0);
|
||||
}
|
||||
GContext.Publish(new ConditionTypeEvent { type = ConditionType.LoginDays, count = 1 });
|
||||
SaveBattleTask();
|
||||
}
|
||||
|
||||
public void RefreshWeeklyTask()
|
||||
{
|
||||
var fishingEvent = _tables.TbFishingEvent.GetOrDefault(BattlePassRecord.ID);
|
||||
if (fishingEvent == null) return;
|
||||
var bpMain = _tables.TbBattlePassMain.GetOrDefault(fishingEvent.RedirectID);
|
||||
if (bpMain == null) return;
|
||||
var weeklyTasks = bpMain.WeeklyTaskList;
|
||||
BattleTask.dic_weeklyTasks.Clear();
|
||||
foreach (var task in weeklyTasks)
|
||||
{
|
||||
BattleTask.dic_weeklyTasks.Add(task, 0);
|
||||
}
|
||||
BattleTask.WeeklyTaskEndTime = ConvertTools.GetDateTimeSunDay(ZZTimeHelper.UtcNow().UtcNowOffset()).AddDays(1);
|
||||
BattlePassRecord.InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
|
||||
GContext.Publish(new ConditionTypeEvent { type = ConditionType.LoginDays, count = 1 });
|
||||
BattleTask.loginDays.Clear();
|
||||
SaveBattleTask();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
public class BattlePassDataProvider : IDisposable
|
||||
{
|
||||
#region Fields
|
||||
|
||||
public BattlePassData Data;
|
||||
private PlayerItemData _itemData;
|
||||
|
||||
public int LockTipID;
|
||||
|
||||
public int DefaultBuyLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
return curPassMain.BuyLevel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Tables _tables;
|
||||
private Dictionary<int, BattlePassLevel> dic_BPLevel;
|
||||
|
||||
public bool IsActivating => Data.BattlePassRecord.ID != 0 && curPassMain != null;
|
||||
public bool UILockVip = false;
|
||||
public bool IsUnlockVip => Data.BattlePassRecord.IsUnlockVip == 1;
|
||||
|
||||
CompositeDisposable disposables = new CompositeDisposable();
|
||||
|
||||
public BattlePassMain curPassMain
|
||||
{
|
||||
get
|
||||
{
|
||||
var fishingEvent = _tables.TbFishingEvent.GetOrDefault(Data.BattlePassRecord.ID);
|
||||
if (fishingEvent == null) { return null; }
|
||||
|
||||
return _tables.TbBattlePassMain.GetOrDefault(fishingEvent.RedirectID);
|
||||
}
|
||||
}
|
||||
public BattlePassLevel curPassLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (curPassMain != null)
|
||||
return dic_BPLevel.GetValueOrDefault(curPassMain.LevelList[Data.BattlePassRecord.Level - 1]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public BattlePassDataProvider(Tables tables, BattlePassData data, PlayerItemData itemData)
|
||||
{
|
||||
_tables = tables;
|
||||
Data = data;
|
||||
dic_BPLevel = tables.TbBattlePassLevel.DataMap;
|
||||
_itemData = itemData;
|
||||
GContext.OnEvent<ConditionTypeEvent>().Subscribe(UpdateTaskData).AddTo(disposables);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Check Func
|
||||
|
||||
public void CheckIfRefreshBattblePass()
|
||||
{
|
||||
if (!IsActivating) return;
|
||||
DateTime endTime = GContext.container.Resolve<FishingEventData>().GetEventEndTime(Data.BattlePassRecord.ID);
|
||||
|
||||
if (ZZTimeHelper.UtcNow().UtcNowOffset() >= endTime && IsActivating)
|
||||
{
|
||||
RecordLastBPUnGetDropIDs();
|
||||
Data.RefreshBattlePassRecord();
|
||||
//GContext.container.Resolve<FishingEventData>().GetEventAndInit(3, 7);
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckIfRefreshDailyTask()
|
||||
{
|
||||
if (ZZTimeHelper.UtcNow().UtcNowOffset() >= Data.BattleTask.DailyTaskEndTime && IsActivating)
|
||||
{
|
||||
Data.RefreshDaiyTask();
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckIfRefreshWeekTask()
|
||||
{
|
||||
|
||||
if (ZZTimeHelper.UtcNow().UtcNowOffset() >= Data.BattleTask.WeeklyTaskEndTime && IsActivating)
|
||||
{
|
||||
Data.RefreshWeeklyTask();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckIfPopBuyVipPanel()
|
||||
{
|
||||
if (Data.BattlePassRecord.BuyVipToastIndex >= curPassMain.RemindLevel.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Data.BattlePassRecord.Level >= curPassMain.RemindLevel[Data.BattlePassRecord.BuyVipToastIndex] && Data.BattlePassRecord.IsUnlockVip == 0;
|
||||
}
|
||||
|
||||
public bool CheckIfGetLastBpRewards()
|
||||
{
|
||||
if (LastBpDropIds.Count != 0)
|
||||
{
|
||||
var playerData = GContext.container.Resolve<PlayerItemData>();
|
||||
ChangeSource changeSource = new ChangeSource(Data.BattlePassRecord.ID, "BattlePass", "BattlePass_Normal28Day", "ExpiredReward");
|
||||
|
||||
var itemDatas = playerData.AddItemByDropListLureInflation(null, LastBpDropIds.GetRange(1, LastBpDropIds.Count - 1), changeSource);
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("bp_level_reward"))
|
||||
{
|
||||
e.AddContent("bp_level", GetBPCurLevel)
|
||||
.AddContent("drop_id_list", JsonConvert.SerializeObject(LastBpDropIds.GetRange(1, LastBpDropIds.Count - 1)));
|
||||
if (itemDatas != null && itemDatas.Count > 0)
|
||||
{
|
||||
int reward_hook = 0;
|
||||
int hook_origin = 0;
|
||||
int reward_cash = 0;
|
||||
for (int i = 0; i < itemDatas.Count; i++)
|
||||
{
|
||||
if (itemDatas[i].id == 1001)
|
||||
{
|
||||
reward_hook += itemDatas[i].count;
|
||||
hook_origin += itemDatas[i].origin;
|
||||
}
|
||||
else if (itemDatas[i].id == 1002)
|
||||
{
|
||||
reward_cash += itemDatas[i].count;
|
||||
}
|
||||
}
|
||||
e.AddContent("reward_hook", reward_hook)
|
||||
.AddContent("hook_origin", hook_origin)
|
||||
.AddContent("reward_cash", reward_cash);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (LastBpDropIds[0] > 0)
|
||||
{
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("bp_cash_reward"))
|
||||
{
|
||||
e.AddContent("bp_level", GetBPCurLevel)
|
||||
.AddContent("cash_num", LastBpDropIds[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
var cash = new ItemData { id = 1002, count = LastBpDropIds[0] };
|
||||
ChangeSource changeSource1 = new ChangeSource(Data.BattlePassRecord.ID, "BattlePass", "BattlePass_Normal28Day", "OverflowReward");
|
||||
|
||||
playerData.AddItem(cash, changeSource1);
|
||||
GContext.Publish(new ShowData(cash));
|
||||
}
|
||||
GContext.Publish(new ShowData(textValue: LocalizationMgr.GetText("UI_BattlePassPanel_25")));
|
||||
LastBpDropIds.Clear();
|
||||
Data.SaveBattlePassRecord();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Get Func
|
||||
|
||||
public TimeSpan GetDailyTaskDuration()
|
||||
{
|
||||
return Data.BattleTask.DailyTaskEndTime - ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
}
|
||||
|
||||
public TimeSpan GetWeeklyTaskDuration()
|
||||
{
|
||||
return Data.BattleTask.WeeklyTaskEndTime - ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
}
|
||||
|
||||
public int GetBPCurLevel => Data.BattlePassRecord.Level;
|
||||
public int GetBPNextLevel => Mathf.Clamp(Data.BattlePassRecord.Level + 1, 1, BPMaxLevel);
|
||||
|
||||
public int BPMaxLevel => curPassMain.LevelList.Count;
|
||||
public List<int> LastBpDropIds => Data.BattlePassRecord.LastDropIDs;
|
||||
|
||||
public BattlePassLevel GetBPLevel(int level) => dic_BPLevel[curPassMain.LevelList[level - 1]];
|
||||
|
||||
public int GetBPCurExp() => Data.BattlePassRecord.Exp;
|
||||
|
||||
public int GetNeedExpToLevelUp(int upLevel)
|
||||
{
|
||||
var curLevel = Data.BattlePassRecord.Level;
|
||||
return GetTotalExpFromLevelRange(curLevel, curLevel + upLevel) - Data.BattlePassRecord.Exp;
|
||||
}
|
||||
|
||||
public int GetCashBoxCount()
|
||||
{
|
||||
var count = 0;
|
||||
if (GetBPCurLevel == BPMaxLevel)
|
||||
count = GetBPCurExp() / 100 * curPassMain.ExpToCash;
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Level Up From StartLevel To EndLevel
|
||||
/// </summary>
|
||||
/// <param name="startLevel"></param>
|
||||
/// <param name="endLevel"></param>
|
||||
/// <returns></returns>
|
||||
public int GetTotalExpFromLevelRange(int startLevel, int endLevel)
|
||||
{
|
||||
var expNeed = 0;
|
||||
endLevel = endLevel > BPMaxLevel ? BPMaxLevel : endLevel;
|
||||
for (int i = startLevel; i < endLevel; i++)
|
||||
{
|
||||
expNeed += dic_BPLevel[curPassMain.LevelList[i - 1]].Exp;
|
||||
}
|
||||
return expNeed;
|
||||
}
|
||||
|
||||
public BPRewardSlot.ESlotState GetSlotState(int level, bool isNormalReward)
|
||||
{
|
||||
// if ( !IsUnlockVip&&!isNormalReward )
|
||||
// return BPRewardSlot.ESlotState.VipLock;
|
||||
if (Data.BattlePassRecord.Level < level || (!isNormalReward && !IsUnlockVip))
|
||||
{
|
||||
return BPRewardSlot.ESlotState.LevelLock;
|
||||
}
|
||||
if (isNormalReward)
|
||||
{
|
||||
if (Data.BattlePassRecord.NormalRewardGet.Contains(level))
|
||||
return BPRewardSlot.ESlotState.HasGotReward;
|
||||
}
|
||||
else if (Data.BattlePassRecord.VipRewardGet.Contains(level))
|
||||
return BPRewardSlot.ESlotState.HasGotReward;
|
||||
return BPRewardSlot.ESlotState.CanGetReward;
|
||||
}
|
||||
|
||||
public void GetAllBpRewards()
|
||||
{
|
||||
var playItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
var dropIds = new List<int>();
|
||||
var dropIds1 = new List<int>();
|
||||
var dropIds2 = new List<int>();
|
||||
List<ItemData> itemDatas = new List<ItemData>();
|
||||
List<ItemData> itemDatas1 = new List<ItemData>();
|
||||
List<ItemData> itemDatas2 = new List<ItemData>();
|
||||
for (int i = 1; i <= Data.BattlePassRecord.Level; i++)
|
||||
{
|
||||
if (GetSlotState(i, true) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
{
|
||||
dropIds1.Add(GetSlotDropID(i, true));
|
||||
SetLevelRewardGet(i, true);
|
||||
}
|
||||
}
|
||||
if (dropIds1.Count > 0)
|
||||
{
|
||||
dropIds.AddRange(dropIds1);
|
||||
ChangeSource changeSource = new ChangeSource(Data.BattlePassRecord.ID, "BattlePass", "BattlePass_Normal28Day", "FreeLevelReward");
|
||||
itemDatas1 = playItemData.GetItemByDropListLureInflation(null, dropIds1);
|
||||
for (int i = 0; i < itemDatas1.Count; i++)
|
||||
{
|
||||
itemDatas1[i].changeSource = changeSource;
|
||||
}
|
||||
itemDatas.AddRange(itemDatas1);
|
||||
}
|
||||
if (IsUnlockVip)
|
||||
{
|
||||
for (int i = 1; i <= Data.BattlePassRecord.Level; i++)
|
||||
{
|
||||
if (GetSlotState(i, false) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
{
|
||||
dropIds2.Add(GetSlotDropID(i, false));
|
||||
SetLevelRewardGet(i, false);
|
||||
}
|
||||
}
|
||||
if (dropIds2.Count > 0)
|
||||
{
|
||||
dropIds.AddRange(dropIds2);
|
||||
ChangeSource changeSource = new ChangeSource(Data.BattlePassRecord.ID, "BattlePass", "BattlePass_Normal28Day", "PrimeLevelReward");
|
||||
itemDatas2 = playItemData.GetItemByDropListLureInflation(null, dropIds2);
|
||||
for (int i = 0; i < itemDatas2.Count; i++)
|
||||
{
|
||||
itemDatas2[i].changeSource = changeSource;
|
||||
}
|
||||
itemDatas.AddRange(itemDatas2);
|
||||
}
|
||||
}
|
||||
Data.SaveBattlePassRecord();
|
||||
GContext.Publish(new ShowData(itemDatas));
|
||||
playItemData.AddItem(itemDatas, null);
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("bp_level_reward"))
|
||||
{
|
||||
e.AddContent("bp_level", GContext.container.Resolve<BattlePassDataProvider>().GetBPCurLevel)
|
||||
.AddContent("drop_id_list", JsonConvert.SerializeObject(dropIds));
|
||||
if (itemDatas != null && itemDatas.Count > 0)
|
||||
{
|
||||
int reward_hook = 0;
|
||||
int hook_origin = 0;
|
||||
int reward_cash = 0;
|
||||
for (int i = 0; i < itemDatas.Count; i++)
|
||||
{
|
||||
if (itemDatas[i].id == 1001)
|
||||
{
|
||||
reward_hook += itemDatas[i].count;
|
||||
hook_origin += itemDatas[i].origin;
|
||||
}
|
||||
else if (itemDatas[i].id == 1002)
|
||||
{
|
||||
reward_cash += itemDatas[i].count;
|
||||
}
|
||||
}
|
||||
e.AddContent("reward_hook", reward_hook)
|
||||
.AddContent("hook_origin", hook_origin)
|
||||
.AddContent("reward_cash", reward_cash);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public int GetAllToGetRewardCount()
|
||||
{
|
||||
var count = 0;
|
||||
for (int i = 1; i <= Data.BattlePassRecord.Level; i++)
|
||||
{
|
||||
if (GetSlotState(i, true) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (IsUnlockVip)
|
||||
{
|
||||
for (int i = 1; i <= Data.BattlePassRecord.Level; i++)
|
||||
{
|
||||
if (GetSlotState(i, false) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public int GetSlotDropID(int level, bool isNormalReward)
|
||||
{
|
||||
if (isNormalReward)
|
||||
{
|
||||
return curPassMain.FreeRewardList[level - 1];
|
||||
}
|
||||
return curPassMain.PrimeRewardList[level - 1];
|
||||
}
|
||||
public ItemData GetSlotDropItem(int level, bool isNormalReward)
|
||||
{
|
||||
if (isNormalReward)
|
||||
{
|
||||
return _itemData.GetItemDataOneLureInflation(null, curPassMain.FreeRewardList[level - 1]);
|
||||
}
|
||||
return _itemData.GetItemDataOneLureInflation(null, curPassMain.PrimeRewardList[level - 1]);
|
||||
}
|
||||
|
||||
public int GetDiamondCostByUpLevel(int upLevel)
|
||||
{
|
||||
var needExp = GetNeedExpToLevelUp(upLevel);
|
||||
if (needExp % 100 > 0)
|
||||
{
|
||||
needExp = (needExp / 100 + 1) * 100;
|
||||
}
|
||||
return needExp / 100 * curPassMain.ExpPrice;
|
||||
}
|
||||
|
||||
public List<ItemData> GetRewardsByUpLevel(int upLevel, bool isNormal)
|
||||
{
|
||||
return GetRewardsByLevelRange(GetBPCurLevel + 1, GetBPCurLevel + upLevel, isNormal);
|
||||
}
|
||||
|
||||
public List<ItemData> GetRewardsByLevelRange(int startLevel, int endLevel, bool isNormal)
|
||||
{
|
||||
|
||||
var curLevel = startLevel;
|
||||
var maxLevel = BPMaxLevel;
|
||||
var curPass = curPassMain;
|
||||
|
||||
Dictionary<int, int> dic_allRewards = new();
|
||||
|
||||
var dic_items = GContext.container.Resolve<Tables>().TbItem.DataMap;
|
||||
var playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
var rewardList = isNormal ? curPass.FreeRewardList : curPass.PrimeRewardList;
|
||||
|
||||
for (int i = curLevel - 1; i < endLevel && i < maxLevel; i++)
|
||||
{
|
||||
var curItemData = playerItemData.GetItemDataOneLureInflation(null, rewardList[i]);
|
||||
if (dic_allRewards.ContainsKey(curItemData.id))
|
||||
{
|
||||
dic_allRewards[curItemData.id] += curItemData.count;
|
||||
}
|
||||
else
|
||||
dic_allRewards.Add(curItemData.id, curItemData.count);
|
||||
}
|
||||
|
||||
var rewards = dic_allRewards.OrderByDescending(x => dic_items[x.Key].Quality).Select(x => new ItemData { id = x.Key, count = x.Value }).ToList();
|
||||
return rewards;
|
||||
}
|
||||
|
||||
public int GetBarsDefaultPos()
|
||||
{
|
||||
for (int i = 1; i <= GetBPCurLevel; i++)
|
||||
{
|
||||
if (GetSlotState(i, true) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
return Mathf.Clamp(i - 1, 1, BPMaxLevel - 3);
|
||||
|
||||
if (GetSlotState(i, false) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
return Mathf.Clamp(i - 1, 1, BPMaxLevel - 3);
|
||||
}
|
||||
return Mathf.Clamp(GetBPCurLevel - 1, 1, BPMaxLevel - 3);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Set Func
|
||||
public void RecordLastBPUnGetDropIDs()
|
||||
{
|
||||
if (curPassMain == null)
|
||||
return;
|
||||
var drops = Data.BattlePassRecord.LastDropIDs;
|
||||
if (Data.BattlePassRecord.ID == 0 || drops.Count > 0)
|
||||
return;
|
||||
drops.Add(GContext.container.Resolve<PlayerItemData>().GetExtraCoinMag(GetCashBoxCount()));
|
||||
for (int i = 1; i <= GetBPCurLevel; i++)
|
||||
{
|
||||
if (GetSlotState(i, true) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
drops.Add(curPassMain.FreeRewardList[i - 1]);
|
||||
|
||||
if (GetSlotState(i, false) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
drops.Add(curPassMain.PrimeRewardList[i - 1]);
|
||||
}
|
||||
|
||||
}
|
||||
public void SetRedPoint()
|
||||
{
|
||||
var flag = 0;
|
||||
for (int i = 1; i <= GetBPCurLevel; i++)
|
||||
{
|
||||
if (GetSlotState(i, false) == BPRewardSlot.ESlotState.CanGetReward || GetSlotState(i, true) == BPRewardSlot.ESlotState.CanGetReward)
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_BattlePass_Reward, true);
|
||||
flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag == 0)
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_BattlePass_Reward, false);
|
||||
|
||||
foreach (var taskInfo in Data.BattleTask.dic_dailyTasks)
|
||||
{
|
||||
var task = _tables.TbBattlePassTask.Get(taskInfo.Key);
|
||||
int requiredCount = int.Parse(task.Param[0]);
|
||||
if (task.Type == ConditionType.ConsumeEnergy)
|
||||
{
|
||||
requiredCount = GContext.container.Resolve<PlayerItemData>().IntInflation(requiredCount, Data.BattlePassRecord.InflationRate);
|
||||
}
|
||||
if (taskInfo.Value >= requiredCount)
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_BattlePass_Task, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
foreach (var taskInfo in Data.BattleTask.dic_weeklyTasks)
|
||||
{
|
||||
var task = _tables.TbBattlePassTask.Get(taskInfo.Key);
|
||||
int requiredCount = int.Parse(task.Param[0]);
|
||||
if (task.Type == ConditionType.ConsumeEnergy)
|
||||
{
|
||||
requiredCount = GContext.container.Resolve<PlayerItemData>().IntInflation(requiredCount, Data.BattlePassRecord.InflationRate);
|
||||
}
|
||||
if (taskInfo.Value >= requiredCount)
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_BattlePass_Task, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_BattlePass_Task, false);
|
||||
}
|
||||
|
||||
public void SetVipUnlock()
|
||||
{
|
||||
Data.BattlePassRecord.IsUnlockVip = 1;
|
||||
Data.SaveBattlePassRecord();
|
||||
}
|
||||
|
||||
public void SetLevelRewardGet(int level, bool isNormal)
|
||||
{
|
||||
if (isNormal)
|
||||
{
|
||||
if (Data.BattlePassRecord.NormalRewardGet.Contains(level))
|
||||
{
|
||||
Debug.LogError("Has Get Reward" + level);
|
||||
return;
|
||||
}
|
||||
Data.BattlePassRecord.NormalRewardGet.Add(level);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Data.BattlePassRecord.VipRewardGet.Contains(level))
|
||||
{
|
||||
Debug.LogError("Has Get Reward" + level);
|
||||
return;
|
||||
}
|
||||
Data.BattlePassRecord.VipRewardGet.Add(level);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetBPExp(int exp)
|
||||
{
|
||||
var curExp = Data.BattlePassRecord.Exp + exp;
|
||||
//Debug.Log("StartExp:" + Data.BattlePassRecord.Exp);
|
||||
var upLevel = 0;
|
||||
var curLevel = GetBPCurLevel;
|
||||
while (curExp - GetTotalExpFromLevelRange(curLevel, curLevel + upLevel + 1) >= 0 && curLevel + upLevel < BPMaxLevel)
|
||||
{
|
||||
upLevel++;
|
||||
}
|
||||
|
||||
Data.BattlePassRecord.Exp = curExp - GetTotalExpFromLevelRange(curLevel, curLevel + upLevel);
|
||||
Data.BattlePassRecord.Level = curLevel + upLevel;
|
||||
//Debug.Log("EndExp:" + Data.BattlePassRecord.Exp + "Get:" + exp + " Level" + Data.BattlePassRecord.Level);
|
||||
Data.SaveBattlePassRecord();
|
||||
SetRedPoint();
|
||||
}
|
||||
|
||||
public void UpdateVipToastLevel()
|
||||
{
|
||||
Data.BattlePassRecord.BuyVipToastIndex++;
|
||||
Data.SaveBattlePassRecord();
|
||||
}
|
||||
|
||||
void UpdateTaskData(ConditionTypeEvent conditionTypeEvent)
|
||||
{
|
||||
if (!IsActivating)
|
||||
return;
|
||||
bool isUpdata = false;
|
||||
var tasks = _tables.TbBattlePassTask;
|
||||
var curTasks = Data.BattleTask.dic_dailyTasks;
|
||||
var curTasksId = Data.BattleTask.dic_dailyTasks.Keys.ToList();
|
||||
//每日任务
|
||||
foreach (var taskId in curTasksId)
|
||||
{
|
||||
var task = tasks.GetOrDefault(taskId);
|
||||
if (task == null)
|
||||
continue;
|
||||
if (task.Type == conditionTypeEvent.type)
|
||||
{
|
||||
int requiredCount = int.Parse(task.Param[0]);
|
||||
if (task.Type == ConditionType.ConsumeEnergy)
|
||||
{
|
||||
requiredCount = GContext.container.Resolve<PlayerItemData>().IntInflation(requiredCount, Data.BattlePassRecord.InflationRate);
|
||||
}
|
||||
if (curTasks[taskId] == requiredCount || curTasks[taskId] == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (task.Type == ConditionType.LoginDays && Data.BattleTask.loginDays.Contains((int)ZZTimeHelper.UtcNow().UtcNowOffset().DayOfWeek))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
curTasks[taskId] += conditionTypeEvent.count;
|
||||
if (curTasks[taskId] >= requiredCount)
|
||||
{
|
||||
curTasks[taskId] = requiredCount;
|
||||
}
|
||||
|
||||
isUpdata = true;
|
||||
}
|
||||
}
|
||||
curTasks = Data.BattleTask.dic_weeklyTasks;
|
||||
curTasksId = Data.BattleTask.dic_weeklyTasks.Keys.ToList();
|
||||
//每周任务
|
||||
foreach (var taskId in curTasksId)
|
||||
{
|
||||
var task = tasks.GetOrDefault(taskId);
|
||||
if (task == null)
|
||||
continue;
|
||||
if (task.Type == conditionTypeEvent.type)
|
||||
{
|
||||
int requiredCount = int.Parse(task.Param[0]);
|
||||
if (task.Type == ConditionType.ConsumeEnergy)
|
||||
{
|
||||
requiredCount = GContext.container.Resolve<PlayerItemData>().IntInflation(requiredCount, Data.BattlePassRecord.InflationRate);
|
||||
}
|
||||
if (curTasks[taskId] == requiredCount || curTasks[taskId] == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (task.Type == ConditionType.LoginDays && Data.BattleTask.loginDays.Contains((int)ZZTimeHelper.UtcNow().UtcNowOffset().DayOfWeek))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
curTasks[taskId] += conditionTypeEvent.count;
|
||||
if (curTasks[taskId] >= requiredCount)
|
||||
{
|
||||
curTasks[taskId] = requiredCount;
|
||||
}
|
||||
|
||||
isUpdata = true;
|
||||
}
|
||||
}
|
||||
if (isUpdata)
|
||||
{
|
||||
if (conditionTypeEvent.type == ConditionType.LoginDays && Data.BattleTask.loginDays.Contains((int)ZZTimeHelper.UtcNow().UtcNowOffset().DayOfWeek))
|
||||
{
|
||||
Data.BattleTask.loginDays.Add((int)ZZTimeHelper.UtcNow().UtcNowOffset().DayOfWeek);
|
||||
}
|
||||
Data.SaveBattleTask();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void SetTaskDone(int taskID, bool isDailyTask)
|
||||
{
|
||||
var tasks = isDailyTask ? Data.BattleTask.dic_dailyTasks : Data.BattleTask.dic_weeklyTasks;
|
||||
tasks[taskID] = -1;
|
||||
Data.SaveBattleTask();
|
||||
}
|
||||
#endregion
|
||||
public void Dispose()
|
||||
{
|
||||
disposables?.Dispose();
|
||||
disposables = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class BattlePassRecord
|
||||
{
|
||||
public int ID;
|
||||
public int Level;
|
||||
public int Exp;
|
||||
//0:Lock,1:Unlock
|
||||
public int IsUnlockVip;
|
||||
public int BuyVipToastIndex;
|
||||
public List<int> NormalRewardGet;
|
||||
public List<int> VipRewardGet;
|
||||
public List<int> LastDropIDs;
|
||||
public float InflationRate;
|
||||
|
||||
public BattlePassRecord()
|
||||
{
|
||||
ID = 0;
|
||||
BuyVipToastIndex = 0;
|
||||
Level = 1;
|
||||
NormalRewardGet = new List<int>();
|
||||
VipRewardGet = new List<int>();
|
||||
LastDropIDs = new();
|
||||
}
|
||||
public void Init(int id)
|
||||
{
|
||||
ID = id;
|
||||
Level = 1;
|
||||
Exp = 0;
|
||||
BuyVipToastIndex = 0;
|
||||
IsUnlockVip = 0;
|
||||
NormalRewardGet.Clear();
|
||||
VipRewardGet.Clear();
|
||||
}
|
||||
public string SerializeObject()
|
||||
{
|
||||
return $"{ID}|{Level}|{Exp}|{BuyVipToastIndex}|{IsUnlockVip}|{string.Join(',', NormalRewardGet)}|{string.Join(',', VipRewardGet)}";
|
||||
}
|
||||
public void DeserializeObject(string data)
|
||||
{
|
||||
var arr = data.Split('|');
|
||||
ID = int.Parse(arr[0]);
|
||||
Level = int.Parse(arr[1]);
|
||||
Exp = int.Parse(arr[2]);
|
||||
BuyVipToastIndex = int.Parse(arr[3]);
|
||||
IsUnlockVip = int.Parse(arr[4]);
|
||||
NormalRewardGet = arr[5].Split(',').Where(x => !string.IsNullOrEmpty(x)).Select(x => int.Parse(x)).ToList();
|
||||
VipRewardGet = arr[6].Split(',').Where(x => !string.IsNullOrEmpty(x)).Select(x => int.Parse(x)).ToList();
|
||||
}
|
||||
}
|
||||
public class BattleTask
|
||||
{
|
||||
public Dictionary<int, int> dic_dailyTasks;
|
||||
public Dictionary<int, int> dic_weeklyTasks;
|
||||
public List<int> loginDays;
|
||||
public DateTime DailyTaskEndTime;
|
||||
public DateTime WeeklyTaskEndTime;
|
||||
public BattleTask()
|
||||
{
|
||||
dic_dailyTasks = new Dictionary<int, int>();
|
||||
dic_weeklyTasks = new Dictionary<int, int>();
|
||||
loginDays = new List<int>();
|
||||
}
|
||||
public string SerializeObject()
|
||||
{
|
||||
string dailyTasksStr = string.Join(',', dic_dailyTasks.Select(x => $"{x.Key}:{x.Value}"));
|
||||
string weeklyTasksStr = string.Join(',', dic_weeklyTasks.Select(x => $"{x.Key}:{x.Value}"));
|
||||
string loginDaysStr = string.Join(',', loginDays);
|
||||
return $"{dailyTasksStr}|{weeklyTasksStr}|{loginDaysStr}|{DailyTaskEndTime.Ticks}|{WeeklyTaskEndTime.Ticks}";
|
||||
}
|
||||
public void DeserializeObject(string data)
|
||||
{
|
||||
var arr = data.Split('|');
|
||||
dic_dailyTasks = arr[0].Split(',').Where(x => !string.IsNullOrEmpty(x)).Select(x =>
|
||||
{
|
||||
var kv = x.Split(':');
|
||||
return new KeyValuePair<int, int>(int.Parse(kv[0]), int.Parse(kv[1]));
|
||||
}).ToDictionary(x => x.Key, x => x.Value);
|
||||
dic_weeklyTasks = arr[1].Split(',').Where(x => !string.IsNullOrEmpty(x)).Select(x =>
|
||||
{
|
||||
var kv = x.Split(':');
|
||||
return new KeyValuePair<int, int>(int.Parse(kv[0]), int.Parse(kv[1]));
|
||||
}).ToDictionary(x => x.Key, x => x.Value);
|
||||
loginDays = arr[2].Split(',').Where(x => !string.IsNullOrEmpty(x)).Select(x => int.Parse(x)).ToList();
|
||||
DailyTaskEndTime = new DateTime(long.Parse(arr[3]));
|
||||
WeeklyTaskEndTime = new DateTime(long.Parse(arr[4]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,596 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class BuffDataCenter
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
private cfg.Tables _tables;
|
||||
public BuffDataCenter(cfg.Tables tables)
|
||||
{
|
||||
this._tables = tables;
|
||||
}
|
||||
Dictionary<int, FishBuffTimeData> mapBuffDataList = new Dictionary<int, FishBuffTimeData>();
|
||||
List<FishBuffTimeData> targetBuffDataList = new List<FishBuffTimeData>();
|
||||
Dictionary<int, FishBuffTimeData> globalBuffDataList = new Dictionary<int, FishBuffTimeData>();
|
||||
FishBuffTimeData fishDailyBuff = null;
|
||||
public List<FishBuffTimeData> showFishBuffIds = new List<FishBuffTimeData>();
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVBuffState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeBuffs(raw,snapshot,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
mapBuffDataList=new Dictionary<int,FishBuffTimeData>();
|
||||
targetBuffDataList=new List<FishBuffTimeData>();
|
||||
globalBuffDataList=new Dictionary<int,FishBuffTimeData>();
|
||||
showFishBuffIds=new List<FishBuffTimeData>(); fishDailyBuff=null;
|
||||
}
|
||||
static FishBuffTimeData MaterializeKVBuff(game.PlayerDataStorage.PlayerKVBuffValue value) => value==null ? null : new FishBuffTimeData
|
||||
{ SortID=value.SortID,sourceID=value.sourceID,buffID=value.buffID,isEnd=value.isEnd,buffEndTime=value.buffEndTime,isNew=false,addDown=0 };
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVBuffState state)
|
||||
{
|
||||
if(state==null) throw new ArgumentNullException(nameof(state));
|
||||
var map=new Dictionary<int,FishBuffTimeData>(); var global=new Dictionary<int,FishBuffTimeData>(); var target=new List<FishBuffTimeData>();
|
||||
foreach(var item in state.Map) map.Add(item.Key,MaterializeKVBuff(item.Value));
|
||||
foreach(var item in state.Global) global.Add(item.Key,MaterializeKVBuff(item.Value));
|
||||
foreach(var item in state.Target) target.Add(MaterializeKVBuff(item));
|
||||
mapBuffDataList=map; globalBuffDataList=global; targetBuffDataList=target; fishDailyBuff=MaterializeKVBuff(state.Daily);
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
internal FishBuffTimeData BuffForKVValidation(int kind,int key)
|
||||
{
|
||||
if(kind==0) return mapBuffDataList.TryGetValue(key,out var value) ? value : null;
|
||||
if(kind==1) return targetBuffDataList.Count>key ? targetBuffDataList[key] : null;
|
||||
if(kind==2) return fishDailyBuff;
|
||||
return globalBuffDataList.TryGetValue(key,out var global) ? global : null;
|
||||
}
|
||||
internal bool IsEmptyForKVValidation => mapBuffDataList.Count==0 && targetBuffDataList.Count==0 &&
|
||||
globalBuffDataList.Count==0 && showFishBuffIds.Count==0 && fishDailyBuff==null;
|
||||
#endif
|
||||
public void InitData(Dictionary<string, string> userDatas)
|
||||
{
|
||||
if (userDatas.TryGetValue("BuffDataList", out string buffDataStr))
|
||||
{
|
||||
InitBuffData(buffDataStr);
|
||||
}
|
||||
if (userDatas.TryGetValue("TargetBuffDataList", out string targetBuffDataStr))
|
||||
{
|
||||
InitTargetBuffData(targetBuffDataStr);
|
||||
}
|
||||
if (userDatas.TryGetValue("FishDailyBuff", out string dailyBuffDataStr))
|
||||
{
|
||||
InitDailyBuffData(dailyBuffDataStr);
|
||||
}
|
||||
if (userDatas.TryGetValue("GlobalBuffDataList", out string globalBuffDataStr))
|
||||
{
|
||||
InitGlobalBuffData(globalBuffDataStr);
|
||||
}
|
||||
|
||||
}
|
||||
void InitBuffData(string dataStr)
|
||||
{
|
||||
mapBuffDataList = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<int, FishBuffTimeData>>(dataStr);
|
||||
}
|
||||
void InitTargetBuffData(string dataStr)
|
||||
{
|
||||
targetBuffDataList = Newtonsoft.Json.JsonConvert.DeserializeObject<List<FishBuffTimeData>>(dataStr);
|
||||
}
|
||||
void InitDailyBuffData(string dataStr)
|
||||
{
|
||||
fishDailyBuff = Newtonsoft.Json.JsonConvert.DeserializeObject<FishBuffTimeData>(dataStr);
|
||||
}
|
||||
void InitGlobalBuffData(string dataStr)
|
||||
{
|
||||
globalBuffDataList = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<int, FishBuffTimeData>>(dataStr);
|
||||
}
|
||||
#region 地图Buff或目标奖励Buff
|
||||
public FishBuff IsMapBuff(int mapId)
|
||||
{
|
||||
if (mapBuffDataList.TryGetValue(mapId, out FishBuffTimeData mapBuffData) && !mapBuffData.isEnd && mapBuffData.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
FishBuff buff = _tables.TbFishBuff.GetOrDefault(mapBuffData.buffID);
|
||||
return buff;
|
||||
}
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
if (!item.isEnd && item.sourceID == mapId && item.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
|
||||
FishBuff buff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
return buff;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public bool IsDarkness(int mapId)
|
||||
{
|
||||
if (mapBuffDataList.TryGetValue(mapId, out FishBuffTimeData mapBuffData) && !mapBuffData.isEnd && mapBuffData.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(mapBuffData.buffID);
|
||||
if (buff.IsDarkness)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
if (!item.isEnd && (item.sourceID == mapId || item.sourceID == 0) && item.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
if (buff.IsDarkness)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public string GetSceneFx(int mapId)
|
||||
{
|
||||
if (mapBuffDataList.TryGetValue(mapId, out FishBuffTimeData mapBuffData) && !mapBuffData.isEnd && mapBuffData.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(mapBuffData.buffID);
|
||||
if (!string.IsNullOrEmpty(buff.SceneFx))
|
||||
{
|
||||
return buff.SceneFx;
|
||||
}
|
||||
}
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
if (!item.isEnd && (item.sourceID == mapId || item.sourceID == 0) && item.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(buff.SceneFx))
|
||||
{
|
||||
return buff.SceneFx;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
//获取某种Buff
|
||||
public FishBuffTimeData GetBuffData<T>() where T : FishBuffType
|
||||
{
|
||||
int curMapId = GContext.container.Resolve<PlayerData>().currentMapId;
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
if (buff.BuffParam is T && !item.isEnd && (item.sourceID == curMapId || item.sourceID == 0) && item.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//排行榜活动特殊地图额外积分
|
||||
public float GetSpMapExtraPointBuffValue()
|
||||
{
|
||||
float extraPointValue = 0;
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
if (buff.BuffParam is SpMapExtraPoint && !item.isEnd && item.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
SpMapExtraPoint extraPoint = buff.BuffParam as SpMapExtraPoint;
|
||||
extraPointValue += extraPoint.Param;
|
||||
}
|
||||
}
|
||||
return extraPointValue;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 地图Buff
|
||||
public bool CheckMapBuff(int mapID)
|
||||
{
|
||||
if (mapID != GContext.container.Resolve<PlayerData>().lastMapId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!mapBuffDataList.ContainsKey(mapID))
|
||||
{
|
||||
var mapdata = _tables.TbMapData.GetOrDefault(mapID);
|
||||
if (mapdata != null && mapdata.FishBuff > 0)
|
||||
{
|
||||
var buffData = _tables.TbFishBuff.GetOrDefault(mapdata.FishBuff);
|
||||
var mapBuffData = new FishBuffTimeData();
|
||||
mapBuffData.SortID = buffData.SortID;
|
||||
mapBuffData.sourceID = mapID;
|
||||
mapBuffData.buffID = mapdata.FishBuff;
|
||||
DateTime now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
mapBuffData.buffEndTime = now.AddSeconds(buffData.CountDown);
|
||||
mapBuffDataList.Add(mapID, mapBuffData);
|
||||
SaveStore.Write("BuffDataList", Newtonsoft.Json.JsonConvert.SerializeObject(mapBuffDataList)).RequireAccepted();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//当前地图的Buff
|
||||
public FishBuffTimeData GetMapBuffTimeData(int mapId)
|
||||
{
|
||||
if (mapBuffDataList.TryGetValue(mapId, out FishBuffTimeData mapBuffData) && !mapBuffData.isEnd && mapBuffData.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
return mapBuffData;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//钓鱼金币加成
|
||||
public float GetExtraGoldBuffValue(int mapId, int fishQuality)
|
||||
{
|
||||
float extraGoldValue = 0;
|
||||
|
||||
if (mapBuffDataList.TryGetValue(mapId, out FishBuffTimeData mapBuffData) && !mapBuffData.isEnd && mapBuffData.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(mapBuffData.buffID);
|
||||
if (buff.BuffParam is ExtraGold)
|
||||
{
|
||||
ExtraGold extraGold = buff.BuffParam as ExtraGold;
|
||||
extraGoldValue += extraGold.Param;
|
||||
}
|
||||
}
|
||||
MoreFishingCash moreFishingCash = GetWeelyBuffTimeData<MoreFishingCash>();
|
||||
if (moreFishingCash != null)
|
||||
{
|
||||
for (int i = 0; i < moreFishingCash.Quality.Count; i++)
|
||||
{
|
||||
if (fishQuality == moreFishingCash.Quality[i])
|
||||
{
|
||||
extraGoldValue += moreFishingCash.Param[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return extraGoldValue;
|
||||
}
|
||||
#endregion
|
||||
#region 目标奖励活动Buff
|
||||
public void CheckTargetBuff()
|
||||
{
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
var collectingTargetInit = fishingEventData.collectingTargetInit;
|
||||
if (collectingTargetInit != null)
|
||||
{
|
||||
EventTargetExtraDrop eventTargetExtraDrop = collectingTargetInit.FishingExtraDrop;
|
||||
ETKeepsake eTKeepsake = null;
|
||||
if (eventTargetExtraDrop is ETKeepsake)
|
||||
{
|
||||
eTKeepsake = eventTargetExtraDrop as ETKeepsake;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int buffID = eTKeepsake.FishbuffID;
|
||||
FishBuff fishBuff = _tables.TbFishBuff.GetOrDefault(buffID);
|
||||
if (fishBuff != null)
|
||||
{
|
||||
FishBuffTimeData fishBuffTimeData = new FishBuffTimeData();
|
||||
if (fishBuff.IsMapBuff)
|
||||
{
|
||||
fishBuffTimeData.sourceID = GContext.container.Resolve<PlayerData>().currentMapId;
|
||||
}
|
||||
else
|
||||
{
|
||||
fishBuffTimeData.sourceID = 0;
|
||||
}
|
||||
fishBuffTimeData.SortID = fishBuff.SortID;
|
||||
fishBuffTimeData.buffID = fishBuff.ID;
|
||||
if (fishBuff.CountDown > 0)
|
||||
{
|
||||
fishBuffTimeData.buffEndTime = ZZTimeHelper.UtcNow().UtcNowOffset().AddSeconds(fishBuff.CountDown);
|
||||
}
|
||||
else
|
||||
{
|
||||
fishBuffTimeData.buffEndTime = fishingEventData.GetCollectingTargetEndTime();
|
||||
}
|
||||
for (int i = 0; i < targetBuffDataList.Count; i++)
|
||||
{
|
||||
if (targetBuffDataList[i].buffID == fishBuffTimeData.buffID && targetBuffDataList[i].sourceID == fishBuffTimeData.sourceID)
|
||||
{
|
||||
targetBuffDataList[i] = fishBuffTimeData;
|
||||
SaveStore.Write("TargetBuffDataList", Newtonsoft.Json.JsonConvert.SerializeObject(targetBuffDataList)).RequireAccepted();
|
||||
return;
|
||||
}
|
||||
}
|
||||
targetBuffDataList.Add(fishBuffTimeData);
|
||||
SaveStore.Write("TargetBuffDataList", Newtonsoft.Json.JsonConvert.SerializeObject(targetBuffDataList)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearTargetBuff();
|
||||
}
|
||||
}
|
||||
public void ClearTargetBuff()
|
||||
{
|
||||
targetBuffDataList.Clear();
|
||||
SaveStore.Write("TargetBuffDataList", Newtonsoft.Json.JsonConvert.SerializeObject(targetBuffDataList)).RequireAccepted();
|
||||
}
|
||||
/// <summary>
|
||||
/// CollectionTargetWeight的特殊处理
|
||||
/// </summary>
|
||||
/// <param name="fishItemId"></param>
|
||||
public void EndCollectionTargetWeight(int fishItemId)
|
||||
{
|
||||
CollectingTargetInit collectingTargetInit = GContext.container.Resolve<FishingEventData>().collectingTargetInit;
|
||||
if (collectingTargetInit == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
EventTargetExtraDrop eventTargetExtraDrop = collectingTargetInit.FishingExtraDrop;
|
||||
ETKeepsake eTKeepsake = null;
|
||||
if (eventTargetExtraDrop is ETKeepsake)
|
||||
{
|
||||
eTKeepsake = eventTargetExtraDrop as ETKeepsake;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
FishBuff fishBuff;
|
||||
int curMapId = GContext.container.Resolve<PlayerData>().currentMapId;
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
fishBuff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
if (!item.isEnd && (item.sourceID == curMapId || item.sourceID == 0) && fishBuff.BuffParam is CollectionTargetWeight)
|
||||
{
|
||||
CollectionTargetWeight collectionTargetWeight = fishBuff.BuffParam as CollectionTargetWeight;
|
||||
if (collectionTargetWeight.TargetFishIndex < 1 || collectionTargetWeight.TargetFishIndex > eTKeepsake.FishIDList.Count)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (fishItemId == eTKeepsake.FishIDList[collectionTargetWeight.TargetFishIndex - 1])
|
||||
{
|
||||
item.isEnd = true;
|
||||
SaveStore.Write("TargetBuffDataList", Newtonsoft.Json.JsonConvert.SerializeObject(targetBuffDataList)).RequireAccepted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//目标奖励活动额外积分
|
||||
public float GetCollectionTargetExtraPointBuffValue(int mapId)
|
||||
{
|
||||
float extraPointValue = 0;
|
||||
foreach (var item in targetBuffDataList)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(item.buffID);
|
||||
if (buff.BuffParam is CollectionTargetExtraPoint && !item.isEnd && (item.sourceID == mapId || item.sourceID == 0) && item.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
CollectionTargetExtraPoint extraPoint = buff.BuffParam as CollectionTargetExtraPoint;
|
||||
extraPointValue += extraPoint.Param;
|
||||
}
|
||||
}
|
||||
return extraPointValue;
|
||||
}
|
||||
#endregion
|
||||
#region 全局Buff Item投放
|
||||
|
||||
public void SetGlobalBucffNew(int buffID, bool isNew = true)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(buffID);
|
||||
if (buff != null)
|
||||
{
|
||||
if (globalBuffDataList.TryGetValue(buff.GroupId, out FishBuffTimeData fishBuffTimeData))
|
||||
{
|
||||
if (fishBuffTimeData.isNew != isNew)
|
||||
{
|
||||
fishBuffTimeData.isNew = isNew;
|
||||
|
||||
if (isNew)
|
||||
{
|
||||
showFishBuffIds.Add(fishBuffTimeData);
|
||||
}
|
||||
else
|
||||
{
|
||||
showFishBuffIds.Remove(fishBuffTimeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddGlobalBuff(int buffID)
|
||||
{
|
||||
var buff = _tables.TbFishBuff.GetOrDefault(buffID);
|
||||
if (buff != null)
|
||||
{
|
||||
var UtcNow = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
bool isNewBuff = true;
|
||||
if (globalBuffDataList.TryGetValue(buff.GroupId, out FishBuffTimeData fishBuffTimeData))
|
||||
{
|
||||
var oldBuff = _tables.TbFishBuff.GetOrDefault(fishBuffTimeData.buffID);
|
||||
if (oldBuff.GroupId != buff.GroupId)
|
||||
{
|
||||
globalBuffDataList.Remove(buff.GroupId);
|
||||
}
|
||||
else
|
||||
{
|
||||
isNewBuff = false;
|
||||
if (fishBuffTimeData.buffEndTime.Ticks > UtcNow.Ticks)
|
||||
{
|
||||
fishBuffTimeData.buffEndTime = fishBuffTimeData.buffEndTime.AddSeconds(buff.CountDown);
|
||||
if (buff.SortID > fishBuffTimeData.SortID)
|
||||
{
|
||||
fishBuffTimeData.SortID = buff.SortID;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fishBuffTimeData.buffEndTime = UtcNow.AddSeconds(buff.CountDown);
|
||||
fishBuffTimeData.SortID = buff.SortID;
|
||||
fishBuffTimeData.isNew = true;
|
||||
}
|
||||
fishBuffTimeData.isEnd = false;
|
||||
showFishBuffIds.Add(fishBuffTimeData);
|
||||
fishBuffTimeData.addDown = buff.CountDown;
|
||||
}
|
||||
}
|
||||
if (isNewBuff)
|
||||
{
|
||||
fishBuffTimeData = new FishBuffTimeData();
|
||||
if (buff.IsMapBuff)
|
||||
{
|
||||
fishBuffTimeData.sourceID = GContext.container.Resolve<PlayerData>().currentMapId;
|
||||
}
|
||||
else
|
||||
{
|
||||
fishBuffTimeData.sourceID = 0;
|
||||
}
|
||||
fishBuffTimeData.buffID = buffID;
|
||||
if (buff.CountDown > 0)
|
||||
{
|
||||
fishBuffTimeData.buffEndTime = ZZTimeHelper.UtcNow().UtcNowOffset().AddSeconds(buff.CountDown);
|
||||
}
|
||||
else
|
||||
{
|
||||
fishBuffTimeData.buffEndTime = ZZTimeHelper.UtcNow().UtcNowOffset().AddDays(1);
|
||||
}
|
||||
globalBuffDataList[buff.GroupId] = fishBuffTimeData;
|
||||
fishBuffTimeData.SortID = buff.SortID;
|
||||
fishBuffTimeData.isNew = true;
|
||||
fishBuffTimeData.addDown = buff.CountDown;
|
||||
showFishBuffIds.Add(fishBuffTimeData);
|
||||
}
|
||||
SaveStore.Write("GlobalBuffDataList", Newtonsoft.Json.JsonConvert.SerializeObject(globalBuffDataList)).RequireAccepted();
|
||||
GContext.Publish(new AddGlobalBuffEvent(buffID));
|
||||
}
|
||||
}
|
||||
|
||||
//GetBuffGroupTimeData
|
||||
public T GetWeelyBuffTimeData<T>(bool isEnd = false) where T : FishBuffType
|
||||
{
|
||||
GetWeelyBuff();
|
||||
foreach (var item in globalBuffDataList)
|
||||
{
|
||||
if (!item.Value.isEnd && item.Value.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
FishBuff buff = _tables.TbFishBuff.GetOrDefault(item.Value.buffID);
|
||||
if (buff != null && buff.BuffParam is T)
|
||||
{
|
||||
//if (isEnd)
|
||||
//{
|
||||
// item.Value.isEnd = true;
|
||||
//}
|
||||
return buff.BuffParam as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public FishBuffTimeData GetBuffTimeDataByType<T>() where T : FishBuffType
|
||||
{
|
||||
GetWeelyBuff();
|
||||
foreach (var item in globalBuffDataList)
|
||||
{
|
||||
if (!item.Value.isEnd && item.Value.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
FishBuff buff = _tables.TbFishBuff.GetOrDefault(item.Value.buffID);
|
||||
if (buff.BuffParam.GetType() == typeof(T))
|
||||
{
|
||||
return item.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public List<FishBuffTimeData> GetGroupBuff()
|
||||
{
|
||||
GetWeelyBuff();
|
||||
List<FishBuffTimeData> buffList = new List<FishBuffTimeData>();
|
||||
foreach (var item in globalBuffDataList)
|
||||
{
|
||||
if (!item.Value.isEnd && item.Value.buffEndTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks)
|
||||
{
|
||||
buffList.Add(item.Value);
|
||||
}
|
||||
}
|
||||
return buffList;
|
||||
}
|
||||
#endregion
|
||||
#region 每日周期Buff
|
||||
|
||||
FishBuffTimeData GetWeelyBuff()
|
||||
{
|
||||
DateTime now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
int week = (int)now.DayOfWeek;
|
||||
var weelyBuff = _tables.TbWeelyBuffInit.GetOrDefault(week);
|
||||
if (weelyBuff != null)
|
||||
{
|
||||
List<int> dailyBuffList = weelyBuff.DailyBuffList;
|
||||
int day = now.DayOfYear;
|
||||
if (fishDailyBuff != null && fishDailyBuff.buffEndTime > now/* && fishDailyBuff.buffEndTime.DayOfYear == day*/)
|
||||
{
|
||||
//旧的每日Buff还未结束
|
||||
return fishDailyBuff;
|
||||
}
|
||||
for (int i = 0; i < dailyBuffList.Count; i++)
|
||||
{
|
||||
var buff = _tables.TbDailyBuffTrigger.GetOrDefault(dailyBuffList[i]);
|
||||
if (buff != null)
|
||||
{
|
||||
DateTime startTime = now.Date.AddSeconds(buff.StartTime);
|
||||
DateTime endTime = now.Date.AddSeconds(buff.EndTime);
|
||||
if (now >= startTime && now <= endTime)
|
||||
{
|
||||
//开启新的每日Buff
|
||||
if (buff.LevelRequired > GContext.container.Resolve<PlayerData>().lv)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (fishDailyBuff == null)
|
||||
{
|
||||
fishDailyBuff = new FishBuffTimeData();
|
||||
}
|
||||
if (fishDailyBuff.sourceID == buff.Day/* && fishDailyBuff.buffEndTime.DayOfYear == day*/)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
FishBuff fishBuff = _tables.TbFishBuff.GetOrDefault(buff.BuffID);
|
||||
fishDailyBuff.buffID = buff.BuffID;
|
||||
fishDailyBuff.sourceID = buff.Day;
|
||||
fishDailyBuff.buffEndTime = endTime;
|
||||
if (fishBuff.CountDown > 0)
|
||||
{
|
||||
fishDailyBuff.buffEndTime = now.AddSeconds(fishBuff.CountDown);
|
||||
}
|
||||
AddGlobalBuff(buff.BuffID);
|
||||
fishDailyBuff.isEnd = false;
|
||||
SaveStore.Write("FishDailyBuff", Newtonsoft.Json.JsonConvert.SerializeObject(fishDailyBuff)).RequireAccepted();
|
||||
return fishDailyBuff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
public class FishBuffTimeData
|
||||
{
|
||||
public int SortID;
|
||||
public int sourceID;
|
||||
public int buffID;
|
||||
public bool isEnd = false;
|
||||
[NonSerialized]
|
||||
public bool isNew = false;
|
||||
[NonSerialized]
|
||||
public int addDown;
|
||||
public DateTime buffEndTime;
|
||||
}
|
||||
public class AddGlobalBuffEvent
|
||||
{
|
||||
|
||||
public int buffID;
|
||||
public AddGlobalBuffEvent(int buffID)
|
||||
{
|
||||
this.buffID = buffID;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,740 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using tysdk;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public class GuideNextEvent
|
||||
{
|
||||
|
||||
}
|
||||
public class AddIndexEvent
|
||||
{
|
||||
public bool CompleteGuide;
|
||||
public int guideSubIndex;
|
||||
public bool isAutoNext;
|
||||
}
|
||||
/// <summary>
|
||||
/// 废弃
|
||||
/// </summary>
|
||||
public struct OnEventTriggerGuide
|
||||
{
|
||||
public OnEventTriggerGuide(string panelName)
|
||||
{
|
||||
this.panelName = panelName;
|
||||
}
|
||||
public string panelName;
|
||||
}
|
||||
public class GuideSaveData
|
||||
{
|
||||
public int guideSubIndex = 0;
|
||||
public bool isFinish = false;
|
||||
public string groupName;
|
||||
}
|
||||
public class GuideDataCenter : IDisposable
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
Tables _tables;
|
||||
PlayerData playerData;
|
||||
public GuideDataCenter(cfg.Tables tables, PlayerData playerData)
|
||||
{
|
||||
this._tables = tables;
|
||||
this.playerData = playerData;
|
||||
}
|
||||
public string CurPanelName;
|
||||
public Dictionary<string, GuideSaveData> GuideSaveDataDic = new Dictionary<string, GuideSaveData>();
|
||||
public GuideSaveData curSaveData;
|
||||
public GuidanceGroupDefine[] guidanceGroupDefines;
|
||||
public GuidanceDefine curDefine;
|
||||
public string popupPanelNode;
|
||||
public GuidanceGroupDefine curGroupDefine;
|
||||
public GuidanceDefineSetting guidanceDefineSetting;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVGuideState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) => game.PlayerDataStorage.PlayerKVSaveSchema.DecodeGuides(raw,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
GuideSaveDataDic = new Dictionary<string,GuideSaveData>(StringComparer.Ordinal);
|
||||
curSaveData = null; curDefine = null; curGroupDefine = null; popupPanelNode = null; CurPanelName = null;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVGuideState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
GuideSaveDataDic = state.Values.ToDictionary(p=>p.Key,
|
||||
p=>new GuideSaveData { guideSubIndex=p.Value.Index, isFinish=p.Value.Finished, groupName=p.Value.Group },StringComparer.Ordinal);
|
||||
}
|
||||
//待释放
|
||||
IDisposable disposable;
|
||||
IDisposable disposable2;
|
||||
IDisposable disposable3;
|
||||
|
||||
public void GMClearGuide()
|
||||
{
|
||||
foreach (var item in guidanceGroupDefines)
|
||||
{
|
||||
if (!GuideSaveDataDic.TryGetValue(item.groupName.ToString(), out GuideSaveData _saveData))
|
||||
{
|
||||
_saveData = new GuideSaveData();
|
||||
_saveData.groupName = item.groupName.ToString();
|
||||
GuideSaveDataDic.Add(item.groupName.ToString(), _saveData);
|
||||
}
|
||||
_saveData.isFinish = true;
|
||||
}
|
||||
GContext.Publish(new ConditionTypeEvent(ConditionType.CompleteGuide, 0));
|
||||
curDefine = null;
|
||||
curGroupDefine = null;
|
||||
Save();
|
||||
}
|
||||
public async void Init()
|
||||
{
|
||||
var store = playerSaveStore;
|
||||
var setting = (GuidanceDefineSetting)await Addressables.LoadAssetAsync<object>("GuidanceConfig");
|
||||
if (disposed || !store.IsCurrent) return;
|
||||
guidanceDefineSetting = setting;
|
||||
if (guidanceDefineSetting != null)
|
||||
{
|
||||
bool isUpdate = false;
|
||||
guidanceGroupDefines = guidanceDefineSetting.guidanceGroupDefines;
|
||||
List<GroupName> Skip = guidanceDefineSetting.SkipGroupNames;
|
||||
bool isSkip = playerData.lv >= guidanceDefineSetting.SkipLevel;
|
||||
if (GuideSaveDataDic == null)
|
||||
{
|
||||
GuideSaveDataDic = new Dictionary<string, GuideSaveData>();
|
||||
}
|
||||
else
|
||||
{
|
||||
GuideSaveData _saveData;
|
||||
GuidanceDefine _define;
|
||||
Dictionary<string, GuideSaveData> keyValuePairs = new Dictionary<string, GuideSaveData>();
|
||||
foreach (var item in guidanceGroupDefines)
|
||||
{
|
||||
|
||||
if (GuideSaveDataDic.TryGetValue(item.groupName.ToString(), out _saveData))
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if (DeferUnavailablePotionGuide(item, _saveData))
|
||||
{
|
||||
keyValuePairs[item.groupName.ToString()] = _saveData;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (!_saveData.isFinish && _saveData.guideSubIndex > 0)
|
||||
{
|
||||
for (int i = _saveData.guideSubIndex; i < item.guidanceDefines.Length; i++)
|
||||
{
|
||||
_define = item.guidanceDefines[i];
|
||||
if (_define.isKey)
|
||||
{
|
||||
_saveData.guideSubIndex = _define.JumpIndex;
|
||||
if (_saveData.guideSubIndex < item.guidanceDefines.Length)
|
||||
{
|
||||
_saveData.isFinish = false;
|
||||
curSaveData = _saveData;
|
||||
curGroupDefine = item;
|
||||
curDefine = curGroupDefine.guidanceDefines[_saveData.guideSubIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
_saveData.isFinish = true;
|
||||
GContext.Publish(new ConditionTypeEvent(ConditionType.CompleteGuide, 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
_saveData.isFinish = true;
|
||||
}
|
||||
}
|
||||
isUpdate = true;
|
||||
}
|
||||
keyValuePairs[item.groupName.ToString()] = _saveData;
|
||||
}
|
||||
else if (isSkip && Skip.Contains(item.groupName))
|
||||
{
|
||||
_saveData = new GuideSaveData();
|
||||
_saveData.groupName = item.groupName.ToString();
|
||||
_saveData.isFinish = true;
|
||||
keyValuePairs[item.groupName.ToString()] = _saveData;
|
||||
isUpdate = true;
|
||||
}
|
||||
}
|
||||
GuideSaveDataDic = keyValuePairs;
|
||||
if (isUpdate)
|
||||
{
|
||||
Save();
|
||||
}
|
||||
}
|
||||
if (disposable2 == null)
|
||||
{
|
||||
disposable2 = GContext.OnEvent<OnEventTriggerGuide>().Subscribe(TriggerGuideNext);
|
||||
}
|
||||
}
|
||||
}
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
private static bool DeferUnavailablePotionGuide(GuidanceGroupDefine group, GuideSaveData saved)
|
||||
{
|
||||
if (!game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled || !game.PlayerDataStorage.PlayerKVGameplayLoop.Ready ||
|
||||
group.groupName != GroupName.EventPotion01 || saved == null || saved.isFinish || saved.guideSubIndex <= 0 ||
|
||||
group.guidanceDefines == null || saved.guideSubIndex >= group.guidanceDefines.Length ||
|
||||
group.guidanceDefines[saved.guideSubIndex] == null ||
|
||||
group.guidanceDefines[saved.guideSubIndex].panelName != "EventPotionPanel")
|
||||
return false;
|
||||
try
|
||||
{
|
||||
// Existing potion activity classification; availability query does not initialize activity data.
|
||||
const int potionEventType = 4;
|
||||
const int potionEventSubType = 5;
|
||||
return GContext.container.Resolve<FishingEventData>().GetEvent(potionEventType, potionEventSubType) == -1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning("Potion guide availability query failed; retaining original guide recovery: " + ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
public bool GetIsFinish(string groupName)
|
||||
{
|
||||
return GuideSaveDataDic.ContainsKey(groupName) && GuideSaveDataDic[groupName].isFinish;
|
||||
}
|
||||
public bool InspectTriggerGuide(string panelName, string prePanelName = "", string curPanelName = "")
|
||||
{
|
||||
if (prePanelName.Contains("FishingRodPanel"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (guidanceDefineSetting == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (curGroupDefine != null
|
||||
&& GuideSaveDataDic.TryGetValue(curGroupDefine.groupName.ToString(), out var guideSaveData)
|
||||
//&& !guideSaveData.isFinish
|
||||
&& curGroupDefine.guidanceDefines.Length > guideSaveData.guideSubIndex
|
||||
&& panelName == curGroupDefine.guidanceDefines[guideSaveData.guideSubIndex].panelName)
|
||||
{
|
||||
CurPanelName = curPanelName;
|
||||
TriggerGuideNext(new OnEventTriggerGuide(panelName));
|
||||
return true;
|
||||
}
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
GuidanceGroupDefine guidanceGroupDefine;
|
||||
for (int i = 0; i < guidanceGroupDefines.Length; i++)
|
||||
{
|
||||
guidanceGroupDefine = guidanceGroupDefines[i];
|
||||
|
||||
bool isFinish = GetIsFinish(guidanceGroupDefine.groupName.ToString());
|
||||
if (isFinish || panelName != guidanceGroupDefine.guidanceDefines[0].panelName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CheckTriggerGuideFinish(guidanceGroupDefine))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int count;
|
||||
switch (guidanceGroupDefine.TriggerConditionsType)
|
||||
{
|
||||
case GuideTriggerConditions.Next:
|
||||
break;
|
||||
case GuideTriggerConditions.First_login:
|
||||
break;
|
||||
case GuideTriggerConditions.OpenSystem_Shop:
|
||||
if (GContext.container.Resolve<PlayerShopData>().IsShopOpen == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
//case GuideTriggerConditions.UnlockSystem_Album:
|
||||
// if (GContext.container.Resolve<AlbumData>().theme == null)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// break;
|
||||
case GuideTriggerConditions.Level_2:
|
||||
if (playerData.lv >= 2)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.Group_Finish:
|
||||
if (GuideSaveDataDic.TryGetValue(guidanceGroupDefine.TriggerConditionsParameter, out var _data) && _data.isFinish)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.GetDiffFishCount:
|
||||
if (int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) && playerFishData.GetDiffFishCount() >= count)
|
||||
{
|
||||
if (playerFishData.GetDiffFishCount() > count)
|
||||
{
|
||||
TriggerGuideFinish(guidanceGroupDefine);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.GetFishCount:
|
||||
if (int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) && playerFishData.GetAnglingCount() >= count)
|
||||
{
|
||||
if (playerFishData.GetAnglingCount() > count)
|
||||
{
|
||||
TriggerGuideFinish(guidanceGroupDefine);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.FirstClickMap_AtMap2:
|
||||
if (playerData.lastMapId == _tables.TbMapData.DataList[1].ID)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.AccountLevel:
|
||||
if (int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) && playerData.lv >= count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.UnlockSystem:
|
||||
if (int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) && GContext.container.Resolve<FishingEventData>().GetSystemOpen(count))
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.UnlockEvent:
|
||||
string[] str = guidanceGroupDefine.TriggerConditionsParameter.Split('-');
|
||||
if (str.Length == 2)
|
||||
{
|
||||
if (int.TryParse(str[0], out count) && int.TryParse(str[1], out int count2) && GContext.container.Resolve<FishingEventData>().GetEvent(count, count2) > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (str.Length == 3)
|
||||
{
|
||||
if (int.TryParse(str[0], out count) && int.TryParse(str[1], out int count2) && int.TryParse(str[2], out int count3) && GContext.container.Resolve<FishingEventData>().GetEventRedirectID(count, count2) == count3)
|
||||
{
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
//if (int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) && GContext.container.Resolve<FishingEventData>().GetSystemOpen(count))
|
||||
//{
|
||||
// break;
|
||||
//}
|
||||
continue;
|
||||
case GuideTriggerConditions.MaxMultiplierCount:
|
||||
if (playerData.IsOpenMagnification &&
|
||||
int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) &&
|
||||
playerData.ContainsOrGreaterRatio(count))
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.GetSecondRod:
|
||||
if (!prePanelName.Contains("HomePanel") && playerFishData.IsOpenRod && playerFishData.GetRodAllCount() > 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.RodAscendAvailable:
|
||||
if (!prePanelName.Contains("HomePanel") && playerFishData.IsOpenRod && playerFishData.GetRodCanUp())
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.RodUpgradeAvailable:
|
||||
if (!prePanelName.Contains("HomePanel") && playerFishData.IsOpenRod && playerFishData.GetRodCanUpLevel())
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.BuyPack:
|
||||
if (int.TryParse(guidanceGroupDefine.TriggerConditionsParameter, out count) && GContext.container.Resolve<PlayerShopData>().GetAllPackCount(count) > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.BoxOpenCondition:
|
||||
FishingBoxDataProvier fishingBoxData = GContext.container.Resolve<FishingBoxDataProvier>();
|
||||
if (fishingBoxData.IsUnclocked && fishingBoxData.GetBoxCount(fishingBoxData.GetIDByQuality(3)) > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.GoBuilding:
|
||||
if (GContext.container.Resolve<CampDataMM>().GoBuilding())
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GuideTriggerConditions.GoBuilding02:
|
||||
if (playerData.lv >= 12)
|
||||
{
|
||||
TriggerGuideFinish(guidanceGroupDefine);
|
||||
Debug.Log("TriggerGuideFinish(guidanceGroupDefine);");
|
||||
continue;
|
||||
}
|
||||
if (GContext.container.Resolve<CampDataMM>().GoBuilding02(101012))
|
||||
{
|
||||
Debug.Log("TriggerGuideFinish();");
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (curGroupDefine == guidanceGroupDefine)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (panelName == guidanceGroupDefine.guidanceDefines[0].panelName)
|
||||
{
|
||||
TriggerGuide(guidanceGroupDefine, curPanelName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void MarkGuideFinished(GroupName groupName)
|
||||
{
|
||||
string key = groupName.ToString();
|
||||
if (GetIsFinish(key)) return;
|
||||
if (!GuideSaveDataDic.TryGetValue(key, out var saveData))
|
||||
{
|
||||
saveData = new GuideSaveData();
|
||||
saveData.groupName = key;
|
||||
GuideSaveDataDic.Add(key, saveData);
|
||||
}
|
||||
saveData.isFinish = true;
|
||||
Save();
|
||||
}
|
||||
|
||||
public void ClearGuide(string groupName)
|
||||
{
|
||||
if (GuideSaveDataDic.ContainsKey(groupName))
|
||||
{
|
||||
GuideSaveDataDic.Remove(groupName);
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 特殊屏蔽的新手引导
|
||||
/// </summary>
|
||||
/// <param name="group"></param>
|
||||
bool CheckTriggerGuideFinish(GuidanceGroupDefine group)
|
||||
{
|
||||
bool isFinishPass = false;
|
||||
//判断是否直接完成
|
||||
if (group.groupName == GroupName.Chanllenge)
|
||||
{
|
||||
// isFinishPass = !GContext.container.Resolve<FishingChallengeCenter>().IsFirstOpen;
|
||||
isFinishPass = !GContext.container.Resolve<FishingChallengeManager>().IsFirstOpen;
|
||||
}
|
||||
|
||||
if (isFinishPass)
|
||||
{
|
||||
TriggerGuideFinish(group);
|
||||
}
|
||||
return isFinishPass;
|
||||
}
|
||||
|
||||
void TriggerGuideFinish(GuidanceGroupDefine group)
|
||||
{
|
||||
string groupName = group.groupName.ToString();
|
||||
if (!GuideSaveDataDic.TryGetValue(groupName, out var guideSaveData))
|
||||
{
|
||||
guideSaveData = new GuideSaveData();
|
||||
guideSaveData.groupName = groupName;
|
||||
GuideSaveDataDic.Add(groupName, guideSaveData);
|
||||
}
|
||||
guideSaveData.guideSubIndex = group.guidanceDefines.Length;
|
||||
guideSaveData.isFinish = true;
|
||||
Save();
|
||||
}
|
||||
public bool TriggerGuide(string groupName, string panelName, bool loop = true,string curPanelName = "")
|
||||
{
|
||||
if (guidanceDefineSetting == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
GuidanceGroupDefine guidanceGroupDefine;
|
||||
if (!loop)
|
||||
{
|
||||
bool isFinish = GetIsFinish(groupName.ToString());
|
||||
if (isFinish)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < guidanceGroupDefines.Length; i++)
|
||||
{
|
||||
guidanceGroupDefine = guidanceGroupDefines[i];
|
||||
if (guidanceGroupDefine.groupName.ToString() == groupName)
|
||||
{
|
||||
if (panelName != guidanceGroupDefine.guidanceDefines[0].panelName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
TriggerGuide(groupName, curPanelName, guidanceGroupDefine);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
async void TriggerGuide(string groupName, string curPanelName, GuidanceGroupDefine guidanceGroupDefine)
|
||||
{
|
||||
var store = playerSaveStore;
|
||||
await System.Threading.Tasks.Task.Delay(200);
|
||||
if (disposed || !store.IsCurrent) return;
|
||||
curSaveData = new GuideSaveData();
|
||||
curSaveData.groupName = groupName;
|
||||
GuideSaveDataDic[groupName] = curSaveData;
|
||||
curGroupDefine = guidanceGroupDefine;
|
||||
curDefine = curGroupDefine.guidanceDefines[0];
|
||||
ShowPanel(curPanelName);
|
||||
curSaveData.guideSubIndex++;
|
||||
}
|
||||
void TriggerGuide(GuidanceGroupDefine group, string curPanelName)
|
||||
{
|
||||
string groupName = group.groupName.ToString();
|
||||
if (!GuideSaveDataDic.TryGetValue(groupName, out var guideSaveData))
|
||||
{
|
||||
guideSaveData = new GuideSaveData();
|
||||
guideSaveData.groupName = groupName;
|
||||
GuideSaveDataDic.Add(groupName, guideSaveData);
|
||||
}
|
||||
if (guideSaveData.isFinish)
|
||||
{
|
||||
return;
|
||||
}
|
||||
guideSaveData.guideSubIndex = 0;
|
||||
curSaveData = guideSaveData;
|
||||
|
||||
curGroupDefine = group;
|
||||
curDefine = group.guidanceDefines[0];
|
||||
ShowPanel(curPanelName);
|
||||
curSaveData.guideSubIndex++;
|
||||
|
||||
}
|
||||
public void TriggerGuideNext(OnEventTriggerGuide eventTriggerGuide)
|
||||
{
|
||||
if (curGroupDefine != null && GuideSaveDataDic.TryGetValue(curGroupDefine.groupName.ToString(), out var guideSaveData))
|
||||
{
|
||||
if (curGroupDefine.guidanceDefines.Length <= curSaveData.guideSubIndex || eventTriggerGuide.panelName != curGroupDefine.guidanceDefines[guideSaveData.guideSubIndex].panelName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
curDefine = curGroupDefine.guidanceDefines[guideSaveData.guideSubIndex];
|
||||
if (curDefine.completeType == CompleteType.PanelOpen)
|
||||
{
|
||||
string uIType = curDefine.completeTypeParameter;
|
||||
if (!string.IsNullOrEmpty(uIType) && UIManager.Instance.ContainsUI(uIType))
|
||||
{
|
||||
curSaveData.guideSubIndex++;
|
||||
AddIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
disposable = GContext.OnEvent<GetUIAsyncEvent>().Subscribe(data =>
|
||||
{
|
||||
if (uIType == data.uIType && data.show)
|
||||
{
|
||||
curSaveData.guideSubIndex++;
|
||||
AddIndex();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowPanel(CurPanelName);
|
||||
curSaveData.guideSubIndex++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InspectTriggerGuide(eventTriggerGuide.panelName);
|
||||
}
|
||||
}
|
||||
public void AddIndex(bool is_pass = false)
|
||||
{
|
||||
if (curDefine == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (disposable != null)
|
||||
{
|
||||
disposable.Dispose();
|
||||
disposable = null;
|
||||
}
|
||||
if (disposable3 != null)
|
||||
{
|
||||
disposable3.Dispose();
|
||||
disposable3 = null;
|
||||
}
|
||||
UIManager.Instance.HideUI(UITypes.GuidancePanel);
|
||||
bool isAutoNext = curDefine.isAutoNext;
|
||||
AddIndexEvent addIndexEvent = new AddIndexEvent();
|
||||
addIndexEvent.isAutoNext = isAutoNext;
|
||||
if (curGroupDefine.guidanceDefines.Length <= curSaveData.guideSubIndex || is_pass)
|
||||
{
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("guide_complete"))
|
||||
{
|
||||
e.AddContent("guide_id", curGroupDefine.groupName.ToString())
|
||||
.AddContent("is_pass", is_pass);
|
||||
}
|
||||
#endif
|
||||
string panelName = curDefine.panelName;
|
||||
curDefine = null;
|
||||
curGroupDefine = null;
|
||||
curSaveData.isFinish = true;
|
||||
Save();
|
||||
if (isAutoNext)
|
||||
{
|
||||
InspectTriggerGuide(panelName, panelName);
|
||||
}
|
||||
GContext.Publish(new ConditionTypeEvent(ConditionType.CompleteGuide, 1));
|
||||
addIndexEvent.CompleteGuide = true;
|
||||
addIndexEvent.guideSubIndex = curSaveData.guideSubIndex;
|
||||
GContext.Publish(addIndexEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Save();
|
||||
var define = curGroupDefine.guidanceDefines[curSaveData.guideSubIndex];
|
||||
if (isAutoNext && define.panelName == curDefine.panelName)
|
||||
{
|
||||
curDefine = define;
|
||||
ShowPanel(CurPanelName);
|
||||
curSaveData.guideSubIndex++;
|
||||
}
|
||||
else if (!isAutoNext)
|
||||
{
|
||||
for (int i = curSaveData.guideSubIndex; i < curGroupDefine.guidanceDefines.Length; i++)
|
||||
{
|
||||
var _define = curGroupDefine.guidanceDefines[i];
|
||||
if (_define.isKey)
|
||||
{
|
||||
curSaveData.isFinish = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
curSaveData.isFinish = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
addIndexEvent.guideSubIndex = curSaveData.guideSubIndex;
|
||||
GContext.Publish(addIndexEvent);
|
||||
}
|
||||
}
|
||||
async void ShowPanel(string PanelName)
|
||||
{
|
||||
if (disposed || !playerSaveStore.IsCurrent) return;
|
||||
if (curDefine.fingerType != FingerType.None)
|
||||
{
|
||||
disposable3 = GContext.OnEvent<GuideNextEvent>().Subscribe(GuideNextEvent);
|
||||
}
|
||||
CurPanelName = PanelName;
|
||||
await UIManager.Instance.ShowUI(UITypes.GuidancePanel);
|
||||
}
|
||||
|
||||
public async void ShowGuidancePopupPanel(string nodeName)
|
||||
{
|
||||
var store = playerSaveStore;
|
||||
if (disposed || !store.IsCurrent) return;
|
||||
popupPanelNode = nodeName;
|
||||
if (!string.IsNullOrEmpty(popupPanelNode))
|
||||
{
|
||||
await UIManager.Instance.ShowUI(UITypes.GuidancePopupPanel);
|
||||
if (disposed || !store.IsCurrent) return;
|
||||
UIManager.Instance.HideUI(UITypes.GuidancePanel);
|
||||
}
|
||||
}
|
||||
|
||||
void GuideNextEvent(GuideNextEvent guideLongEvent)
|
||||
{
|
||||
if (curDefine != null && curDefine.fingerType != FingerType.None)
|
||||
{
|
||||
AddIndex();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsGuide(string panelName)
|
||||
{
|
||||
bool isGuide = curDefine != null;
|
||||
if (!isGuide)
|
||||
{
|
||||
InspectTriggerGuide(panelName);
|
||||
}
|
||||
return curDefine != null && curDefine.panelName == panelName;
|
||||
}
|
||||
|
||||
public bool IsGuide(string panelName, int index, GroupName groupName)
|
||||
{
|
||||
bool isGuide = curDefine != null;
|
||||
|
||||
if (isGuide && curGroupDefine.groupName == groupName && curSaveData.guideSubIndex == index)
|
||||
{
|
||||
InspectTriggerGuide(panelName);
|
||||
return true;
|
||||
}
|
||||
return false;// curDefine != null && curDefine.panelName == panelName;
|
||||
}
|
||||
public void SetData(string value)
|
||||
{
|
||||
string[] groupStrings = value.Split(';');
|
||||
foreach (var groupString in groupStrings)
|
||||
{
|
||||
string[] strings = groupString.Split('|');
|
||||
if (strings.Length == 3)
|
||||
{
|
||||
GuideSaveData saveData = new GuideSaveData();
|
||||
saveData.groupName = strings[0];
|
||||
if (int.TryParse(strings[1], out int guideSubIndex))
|
||||
{
|
||||
saveData.guideSubIndex = guideSubIndex;
|
||||
}
|
||||
saveData.isFinish = strings[2] == "1";
|
||||
GuideSaveDataDic[saveData.groupName] = saveData;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Save()
|
||||
{
|
||||
List<string> strings = GuideSaveDataDic.Select(kv => kv.Key + "|" + kv.Value.guideSubIndex + "|" + (kv.Value.isFinish ? "1" : "0")).ToList();
|
||||
string value = string.Join(";", strings);
|
||||
playerSaveStore.Write("GuideSaveDataNew", value).RequireAccepted();
|
||||
}
|
||||
|
||||
bool disposed;
|
||||
public void Dispose()
|
||||
{
|
||||
disposed = true;
|
||||
disposable?.Dispose();
|
||||
disposable = null;
|
||||
disposable2?.Dispose();
|
||||
disposable2 = null;
|
||||
disposable3?.Dispose();
|
||||
disposable3 = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,232 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public class DailySignEvent
|
||||
{
|
||||
public int ID;
|
||||
//领取第几个
|
||||
public int index;
|
||||
//签到最后一次是哪一天or链式礼包PackManager.ID
|
||||
public int lastID;
|
||||
//结束时间
|
||||
public string time;
|
||||
}
|
||||
public class MonthlySignEvent
|
||||
{
|
||||
public int index;
|
||||
//结束时间
|
||||
public DailySignEvent dailySignEvent;
|
||||
}
|
||||
public class OnSignEvent
|
||||
{
|
||||
public List<ItemData> itemDataList;
|
||||
}
|
||||
public class SigninData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
private cfg.Tables _tables;
|
||||
public SigninData(cfg.Tables tables)
|
||||
{
|
||||
this._tables = tables;
|
||||
}
|
||||
|
||||
MonthlySignEvent _monthlySignEvent = null;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVMonthlySignState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) => game.PlayerDataStorage.PlayerKVSaveSchema.DecodeMonthlySign(raw,errors);
|
||||
internal void ResetForKVLoad() { _monthlySignEvent = null; }
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVMonthlySignState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
_monthlySignEvent = !state.IsPresent ? null : new MonthlySignEvent
|
||||
{
|
||||
index = state.Index,
|
||||
dailySignEvent = new DailySignEvent { ID=state.DailyId, index=state.DailyIndex, lastID=state.LastId, time=state.Time }
|
||||
};
|
||||
}
|
||||
|
||||
public DailySignEvent dailySignEvent
|
||||
{
|
||||
get => _monthlySignEvent == null ? null : _monthlySignEvent.dailySignEvent;
|
||||
}
|
||||
|
||||
public MonthlySignEvent monthlySignEvent
|
||||
{
|
||||
get => _monthlySignEvent;
|
||||
set => _monthlySignEvent = value;
|
||||
}
|
||||
public void SetDailySignEvent(FishingEvent t)
|
||||
{
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
bool isSave = false;
|
||||
DailySignEvent data = null;
|
||||
if (_monthlySignEvent == null)
|
||||
{
|
||||
_monthlySignEvent = new MonthlySignEvent()
|
||||
{
|
||||
index = 0,
|
||||
};
|
||||
isSave = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<MonthlySign> MonthlySignL = _tables.TbMonthlySign.DataList;
|
||||
|
||||
data = _monthlySignEvent.dailySignEvent;
|
||||
if (_monthlySignEvent.index >= MonthlySignL[^1].SignDayAccount && data.lastID != dateTime.DayOfYear)
|
||||
{
|
||||
_monthlySignEvent = new MonthlySignEvent()
|
||||
{
|
||||
index = 0,
|
||||
dailySignEvent = data,
|
||||
};
|
||||
isSave = true;
|
||||
}
|
||||
}
|
||||
SetTheme();
|
||||
if (data == null || (data.index >= 7 && data.lastID != dateTime.DayOfYear))
|
||||
{
|
||||
_monthlySignEvent.dailySignEvent = new DailySignEvent()
|
||||
{
|
||||
ID = t.RedirectID,
|
||||
lastID = -1,
|
||||
index = 0,
|
||||
time = dateTime.Date.ToString()
|
||||
};
|
||||
isSave = true;
|
||||
}
|
||||
if (isSave)
|
||||
{
|
||||
SaveStore.Write("MonthlySignEvent", JsonMapper.ToJson(_monthlySignEvent)).RequireAccepted();
|
||||
}
|
||||
bool isReward = CanDailySign();
|
||||
if (isReward)
|
||||
{
|
||||
ILoadResourceService loadResourceService = GContext.container.Resolve<ILoadResourceService>();
|
||||
loadResourceService.EnqueueBundleSilently(new List<string>() { UITypes.EventSignupPopupPanel_2.Name });
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(t.ID, UITypes.EventSignupPopupPanel_2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//签到
|
||||
public bool OnDailySign(int index, int DropID)
|
||||
{
|
||||
if (_monthlySignEvent == null || _monthlySignEvent.dailySignEvent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool isSign = SignEvent(_monthlySignEvent.dailySignEvent, index);
|
||||
if (isSign)
|
||||
{
|
||||
_monthlySignEvent.index++;
|
||||
List<ItemData> itemDataList = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(null, DropID, null, false);
|
||||
#if AGG
|
||||
DateTime tomorrow = GlobalUtils.TryParseDateTime(_monthlySignEvent.dailySignEvent.time, ZZTimeHelper.UtcNow().UtcNowOffset());
|
||||
TimeSpan remaining = ZZTimeHelper.UtcNow().UtcNowOffset() - tomorrow;
|
||||
using (var e = GEvent.GameEvent("user_checkin"))
|
||||
{
|
||||
e.AddContent("day_num", remaining.Days)
|
||||
.AddContent("day_count", _monthlySignEvent.dailySignEvent.index);
|
||||
if (itemDataList != null)
|
||||
{
|
||||
for (int i = 0; i < itemDataList.Count; i++)
|
||||
{
|
||||
e.AddContent($"item_{itemDataList[i].id}", itemDataList[i].count);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
List<MonthlySign> MonthlySignL = _tables.TbMonthlySign.DataList;
|
||||
List<ItemData> monthlySignReward = null;
|
||||
for (int i = 0; i < MonthlySignL.Count; i++)
|
||||
{
|
||||
if (_monthlySignEvent.index == MonthlySignL[i].SignDayAccount)
|
||||
{
|
||||
monthlySignReward = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(null, MonthlySignL[i].DropID, null, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//GContext.Publish(new ShowData());
|
||||
SaveStore.Write("MonthlySignEvent", JsonMapper.ToJson(_monthlySignEvent)).RequireAccepted();
|
||||
GContext.Publish(new OnSignEvent() { itemDataList = monthlySignReward });
|
||||
}
|
||||
return isSign;
|
||||
}
|
||||
|
||||
|
||||
bool SignEvent(DailySignEvent data, int index)
|
||||
{
|
||||
if (data.lastID == ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear)
|
||||
{
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_1"));
|
||||
return false;
|
||||
}
|
||||
if (index == data.index)
|
||||
{
|
||||
data.lastID = ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear;
|
||||
data.index++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public bool DailySignIsOpen()
|
||||
{
|
||||
return _monthlySignEvent != null && _monthlySignEvent.dailySignEvent != null;
|
||||
}
|
||||
|
||||
public bool CanDailySign()
|
||||
{
|
||||
DailySignEvent data = dailySignEvent;
|
||||
return data != null
|
||||
&& data.lastID != ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear
|
||||
&& data.index < 7;
|
||||
}
|
||||
|
||||
public bool ShouldLoadSignInInterstitial()
|
||||
{
|
||||
return DailySignIsOpen() && FishingEventData.Condition_NoRechargeDay(15);
|
||||
}
|
||||
|
||||
private const string DefaultPanel = "EventSignupPopupPanel_2";
|
||||
private void SetTheme()
|
||||
{
|
||||
var themeList = _tables.TbEventSignSpTheme.DataList;
|
||||
UITypes.EventSignupPopupPanel_2.SetType(DefaultPanel);
|
||||
foreach (var theme in themeList)
|
||||
{
|
||||
try
|
||||
{
|
||||
var startTime = DateTime.Parse((theme.TimeDefinition as LimitedTime).StartTime);
|
||||
var endTime = DateTime.Parse((theme.TimeDefinition as LimitedTime).EndTime);
|
||||
var timeNow = ZZTimeHelper.UtcNow();
|
||||
if (startTime <= timeNow && timeNow <= endTime)
|
||||
{
|
||||
UITypes.EventSignupPopupPanel_2.SetType(theme.Prefab);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[DataCenter] SetSignInTheme Error:");
|
||||
UnityEngine.Debug.LogError(e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void ResetTheme(TimeSpan duration)
|
||||
{
|
||||
await System.Threading.Tasks.Task.Delay(duration);
|
||||
UITypes.EventSignupPopupPanel_2.SetType(DefaultPanel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,706 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using LitJson;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using static EventPartnerData;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public class ChangeSmallGameTargetEvent
|
||||
{
|
||||
public OpponentItemData opponentItemData;
|
||||
public System.Threading.Tasks.TaskCompletionSource<bool> ts;
|
||||
public bool random;
|
||||
}
|
||||
public class SmallGameData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
private cfg.Tables _tables;
|
||||
IUserService userService;
|
||||
ICustomServerMgr customServerMgr;
|
||||
public SmallGameData(cfg.Tables tables, IUserService userService, ICustomServerMgr customServerMgr)
|
||||
{
|
||||
this._tables = tables;
|
||||
this.userService = userService;
|
||||
this.customServerMgr = customServerMgr;
|
||||
BottleCashParamBase = _tables.TbGlobalConfig.BottleCashParamBase;
|
||||
BottleCashParamDiv = _tables.TbGlobalConfig.BottleCashParamDiv;
|
||||
BottleCashParamLevel = _tables.TbGlobalConfig.BottleCashParamLevel;
|
||||
}
|
||||
public void Init()
|
||||
{
|
||||
campData = GContext.container.Resolve<CampDataMM>();
|
||||
|
||||
LoginRandomOpponent();
|
||||
}
|
||||
CampDataMM campData;
|
||||
|
||||
int _heistGameCount = 0;
|
||||
int _bombGameCount = 0;
|
||||
int _fishHuntGameCount = 0;
|
||||
public int HeistGameCount => _heistGameCount;
|
||||
public int FishHuntGameCount => _fishHuntGameCount;
|
||||
public int BombGameCount => _bombGameCount;
|
||||
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
_heistGameCount=_bombGameCount=_fishHuntGameCount=0;
|
||||
opponentItemDatas=new List<OpponentItemData>(); opponentFriendItemDatas=new List<OpponentItemData>();
|
||||
opponentNotFriendItemDatas=new List<OpponentItemData>(); opponentFriend=new List<OpponentItemData>();
|
||||
opponentFriendDic=new Dictionary<string,OpponentItemData>(); opponentEnemy=new Dictionary<EnemyType,List<OpponentItemData>>();
|
||||
opponentItemDatasPre=new List<OpponentItemData>(); opponentFriendItemDatasPre=new List<OpponentItemData>();
|
||||
opponentNotFriendItemDatasPre=new List<OpponentItemData>(); powerData=new Dictionary<string,float>();
|
||||
opponentData=null; nextOpponent=null; curEnemyType=EnemyType.Aquarium; RobotMapId=nextRobotMapId=0;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVCoreState state)
|
||||
{
|
||||
if(state==null) throw new ArgumentNullException(nameof(state));
|
||||
_heistGameCount=state.Integer(16); _bombGameCount=state.Integer(17); _fishHuntGameCount=state.Integer(18);
|
||||
}
|
||||
internal bool IsOpponentEmptyForKVValidation => opponentItemDatas.Count==0 && opponentFriendItemDatas.Count==0 &&
|
||||
opponentNotFriendItemDatas.Count==0 && opponentFriend.Count==0 && opponentFriendDic.Count==0 && opponentEnemy.Count==0 &&
|
||||
opponentItemDatasPre.Count==0 && opponentFriendItemDatasPre.Count==0 && opponentNotFriendItemDatasPre.Count==0 &&
|
||||
powerData.Count==0 && opponentData==null && nextOpponent==null && RobotMapId==0 && nextRobotMapId==0;
|
||||
|
||||
//public UnityEngine.Vector3 image_extra_pos { set; get; }
|
||||
|
||||
public void SetHeistGameCount(int value)
|
||||
{
|
||||
_heistGameCount = value;
|
||||
}
|
||||
public void SaveHeistGameCount()
|
||||
{
|
||||
_heistGameCount++;
|
||||
playerSaveStore.Write("HeistGameCount", _heistGameCount.ToString()).RequireAccepted();
|
||||
}
|
||||
public void SetBombGameCount(int value)
|
||||
{
|
||||
_bombGameCount = value;
|
||||
}
|
||||
public void SaveBombGameCount()
|
||||
{
|
||||
_bombGameCount++;
|
||||
playerSaveStore.Write("BombGameCount", _bombGameCount.ToString()).RequireAccepted();
|
||||
}
|
||||
public void SetFishHuntGameCount(int value)
|
||||
{
|
||||
_fishHuntGameCount = value;
|
||||
}
|
||||
public void SaveFishHuntGameCount()
|
||||
{
|
||||
_fishHuntGameCount++;
|
||||
playerSaveStore.Write("FishHuntGameCount", _fishHuntGameCount.ToString()).RequireAccepted();
|
||||
}
|
||||
#region 小游戏仇人
|
||||
public List<OpponentItemData> opponentItemDatas = new List<OpponentItemData>();
|
||||
public List<OpponentItemData> opponentFriendItemDatas = new List<OpponentItemData>();
|
||||
public List<OpponentItemData> opponentNotFriendItemDatas = new List<OpponentItemData>();
|
||||
public List<OpponentItemData> opponentFriend = new List<OpponentItemData>();
|
||||
Dictionary<string, OpponentItemData> opponentFriendDic = new Dictionary<string, OpponentItemData>();
|
||||
public Dictionary<EnemyType, List<OpponentItemData>> opponentEnemy = new Dictionary<EnemyType, List<OpponentItemData>>();
|
||||
List<OpponentItemData> opponentItemDatasPre = new List<OpponentItemData>();
|
||||
List<OpponentItemData> opponentFriendItemDatasPre = new List<OpponentItemData>();
|
||||
List<OpponentItemData> opponentNotFriendItemDatasPre = new List<OpponentItemData>();
|
||||
EnemyType curEnemyType = EnemyType.Aquarium;
|
||||
public Dictionary<string, float> powerData;
|
||||
public OpponentItemData opponentData;
|
||||
OpponentItemData nextOpponent;
|
||||
int GMbobmName = 0;
|
||||
int BottleCashParamBase;
|
||||
int BottleCashParamDiv;
|
||||
int BottleCashParamLevel;
|
||||
public int RobotMapId;
|
||||
public int nextRobotMapId;
|
||||
public void GMSetBomb(string bobmName)
|
||||
{
|
||||
GMbobmName = int.Parse(bobmName);
|
||||
}
|
||||
|
||||
public async void LoginRandomOpponent()
|
||||
{
|
||||
GetRobotData();
|
||||
RandomOpponent();
|
||||
await LoadData();
|
||||
RandomOpponent();
|
||||
}
|
||||
|
||||
void RandomOpponent()
|
||||
{
|
||||
Synchrodata();
|
||||
nextOpponent = NextOpponent();
|
||||
LoadNextOpponentResource();
|
||||
}
|
||||
|
||||
public List<OpponentItemData> GetOpponentEnemy()
|
||||
{
|
||||
return opponentEnemy.GetValueOrDefault(curEnemyType);
|
||||
}
|
||||
|
||||
public OpponentItemData GetBankHeistOpponent(EnemyType enemyType)
|
||||
{
|
||||
this.curEnemyType = enemyType;
|
||||
if (opponentItemDatasPre == null || opponentItemDatasPre.Count == 0)
|
||||
{
|
||||
ClearOpponent();
|
||||
GetFriendData();
|
||||
GetRobotData();
|
||||
}
|
||||
opponentData = RandomItem();
|
||||
LoadData();
|
||||
GetEnemyData(curEnemyType);
|
||||
if (GMbobmName > 0)
|
||||
{
|
||||
opponentData.BombID = GMbobmName;
|
||||
}
|
||||
return opponentData;
|
||||
}
|
||||
|
||||
void Synchrodata()
|
||||
{
|
||||
if (opponentItemDatasPre != null && opponentItemDatasPre.Count > 0)
|
||||
{
|
||||
opponentItemDatas = opponentItemDatasPre;
|
||||
}
|
||||
if (opponentFriendItemDatasPre != null && opponentFriendItemDatasPre.Count > 0)
|
||||
{
|
||||
opponentFriendItemDatas = opponentFriendItemDatasPre;
|
||||
}
|
||||
if (opponentNotFriendItemDatasPre != null && opponentNotFriendItemDatasPre.Count > 0)
|
||||
{
|
||||
opponentNotFriendItemDatas = opponentNotFriendItemDatasPre;
|
||||
}
|
||||
}
|
||||
public OpponentItemData RandomItem()
|
||||
{
|
||||
OpponentItemData target = nextOpponent;
|
||||
Synchrodata();
|
||||
nextOpponent = NextOpponent();
|
||||
RobotMapId = nextRobotMapId;
|
||||
LoadNextOpponentResource();
|
||||
return target;
|
||||
}
|
||||
|
||||
async void LoadNextOpponentResource()
|
||||
{
|
||||
if (nextOpponent != null)
|
||||
{
|
||||
ILoadResourceService loadResourceService = GContext.container.Resolve<ILoadResourceService>();
|
||||
RobotSetMap();
|
||||
int mapId;
|
||||
if (nextOpponent.isRobot)
|
||||
{
|
||||
mapId = nextRobotMapId;
|
||||
}
|
||||
else
|
||||
{
|
||||
mapId = await LoadAquariumResource(nextOpponent.playFabID);
|
||||
if (mapId > 0)
|
||||
{
|
||||
nextRobotMapId = mapId;
|
||||
}
|
||||
else
|
||||
{
|
||||
mapId = nextRobotMapId;
|
||||
}
|
||||
}
|
||||
Debug.Log($"nextOpponent Next MapID {mapId}");
|
||||
|
||||
if (mapId > 0)
|
||||
{
|
||||
MapDownloadRes mapData = _tables.TbMapDownloadRes.GetOrDefault(mapId);
|
||||
if (mapData != null)
|
||||
{
|
||||
loadResourceService.EnqueueBundleSilently(mapData.FishRes);
|
||||
}
|
||||
}
|
||||
|
||||
string bombPrefab = _tables.TbConstruction.GetOrDefault(nextOpponent.BombID)?.BombPrefab;
|
||||
if (!string.IsNullOrEmpty(bombPrefab))
|
||||
{
|
||||
loadResourceService.EnqueueBundleSilently(new List<string>() { bombPrefab });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RobotSetMap()
|
||||
{
|
||||
int curMapId = nextRobotMapId;
|
||||
|
||||
var mapDatas = _tables.TbMapData.DataList;
|
||||
var maxMapID = GContext.container.Resolve<PlayerData>().lastMapId;
|
||||
|
||||
//非当前地图
|
||||
var mapIDs = new List<int>();
|
||||
for (int i = 0; i < mapDatas.Count; i++)
|
||||
{
|
||||
if (mapDatas[i].ID <= maxMapID)
|
||||
{
|
||||
if (mapDatas[i].ID != curMapId)
|
||||
{
|
||||
mapIDs.Add(mapDatas[i].ID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mapIDs.Count == 0)
|
||||
{
|
||||
nextRobotMapId = maxMapID;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextRobotMapId = mapIDs[UnityEngine.Random.Range(0, mapIDs.Count)];
|
||||
}
|
||||
}
|
||||
|
||||
async Task<int> LoadAquariumResource(string playFabId)
|
||||
{
|
||||
GetAquariumDataRequest request = new GetAquariumDataRequest();
|
||||
request.playFabId = playFabId;
|
||||
string result = await customServerMgr.CustomServerPost("aquarium/GetAquariumData", Newtonsoft.Json.JsonConvert.SerializeObject(request));
|
||||
if (result != null)
|
||||
{
|
||||
var resp = JsonConvert.DeserializeObject<GetAquariumDataResp>(result);
|
||||
if (resp.code == 0)
|
||||
{
|
||||
var data = resp.data;
|
||||
List<AquariumSlotData> slotDatas = data.slotDatas;
|
||||
if (slotDatas == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var time = ZZTimeHelper.UtcNow();
|
||||
List<AquariumResultData> aquariumResultDatas = data.aquariumResultDatas;
|
||||
for (int i = 0; i < slotDatas.Count; i++)
|
||||
{
|
||||
AquariumSlotData slotData = slotDatas[i];
|
||||
slotData.index = i;
|
||||
var HatchTime = slotData.FishHatchTime();
|
||||
|
||||
if (HatchTime < time)
|
||||
{
|
||||
if (!slotData.IsResult() && aquariumResultDatas.Count > i && !aquariumResultDatas[i].IsResult())
|
||||
{
|
||||
return resp.data.mapId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public OpponentItemData NextOpponent()
|
||||
{
|
||||
int friendW = Math.Min(100, opponentFriendItemDatas.Count * 20);
|
||||
if (opponentFriendItemDatas.Count == 0)
|
||||
{
|
||||
friendW = 0;
|
||||
}
|
||||
int noFriendW = 70;
|
||||
if (opponentNotFriendItemDatas.Count == 0)
|
||||
{
|
||||
noFriendW = 0;
|
||||
}
|
||||
int robotW = 30;
|
||||
int allW = friendW + noFriendW + robotW;
|
||||
int w = UnityEngine.Random.Range(0, allW);
|
||||
OpponentItemData target;
|
||||
if (friendW > 0 && opponentFriendItemDatas.Count > 0 && w < friendW)
|
||||
{
|
||||
target = opponentFriendItemDatas[UnityEngine.Random.Range(0, opponentFriendItemDatas.Count)];
|
||||
target.target_group = 1;
|
||||
}
|
||||
else if (noFriendW > 0 && opponentNotFriendItemDatas.Count > 0 && w < friendW + noFriendW)
|
||||
{
|
||||
target = opponentNotFriendItemDatas[UnityEngine.Random.Range(0, opponentNotFriendItemDatas.Count)];
|
||||
target.target_group = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (opponentItemDatas == null || opponentItemDatas.Count == 0)
|
||||
{
|
||||
GetRobotData();
|
||||
opponentItemDatas = opponentItemDatasPre;
|
||||
Debug.LogError("opponentItemDatas Count == 0");
|
||||
}
|
||||
target = opponentItemDatas[UnityEngine.Random.Range(0, opponentItemDatas.Count)];
|
||||
target.target_group = 3;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
void ClearOpponent()
|
||||
{
|
||||
Synchrodata();
|
||||
opponentFriendItemDatasPre = new List<OpponentItemData>();
|
||||
opponentNotFriendItemDatasPre = new List<OpponentItemData>();
|
||||
opponentItemDatasPre = new List<OpponentItemData>();
|
||||
powerData = new Dictionary<string, float>();
|
||||
opponentFriend = new List<OpponentItemData>();
|
||||
opponentFriendDic = new Dictionary<string, OpponentItemData>();
|
||||
}
|
||||
public async Task LoadData()
|
||||
{
|
||||
ClearOpponent();
|
||||
GetFriendData();
|
||||
try
|
||||
{
|
||||
await GetActiveNotFriendData();
|
||||
await GetLeaderboardData(curEnemyType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(ex.Message);
|
||||
}
|
||||
GetRobotData();
|
||||
}
|
||||
|
||||
async Task GetActiveNotFriendData()
|
||||
{
|
||||
var request = new EventBuildRecommendPlayerRequest();
|
||||
int lv = GContext.container.Resolve<PlayerData>().lv;
|
||||
int minLevel = (int)(lv * 0.5f);
|
||||
if (minLevel < 0)
|
||||
{
|
||||
minLevel = 0;
|
||||
}
|
||||
request.MinLevel = minLevel;
|
||||
request.MaxLevel = lv * 2;
|
||||
request.Exclude = new List<string>();
|
||||
|
||||
string json = await customServerMgr.CustomServerPost($"eventpartner/recommend",
|
||||
Newtonsoft.Json.JsonConvert.SerializeObject(request));
|
||||
if (!string.IsNullOrEmpty(json) && json != "[]")
|
||||
{
|
||||
EventBuildRecommendPlayerResp resp = Newtonsoft.Json.JsonConvert.DeserializeObject<EventBuildRecommendPlayerResp>(json);
|
||||
if (resp.State == EEventBuildState.Success && resp.Players != null)
|
||||
{
|
||||
List<Construction> _dataList = _tables.TbConstruction.DataList;
|
||||
for (int i = 0; i < resp.Players.Count; i++)
|
||||
{
|
||||
if (resp.Players[i].PlayFabId == userService.UserId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
OpponentItemData opponentItemData = new OpponentItemData();
|
||||
opponentItemData.playFabID = resp.Players[i].PlayFabId;
|
||||
opponentItemData.target_source = 2;
|
||||
|
||||
userService.SetPlayInfo(resp.Players[i].PlayFabId, resp.Players[i].DisplayName, resp.Players[i].AvatarUrl);
|
||||
userService.SetPlayerLv(resp.Players[i].PlayFabId, resp.Players[i].Level);
|
||||
int level = resp.Players[i].Level;
|
||||
|
||||
opponentItemData.BombID = GetConstructionID(level);
|
||||
opponentItemData.power = (float)Math.Log10(BottleCashParamBase + level / BottleCashParamLevel) / BottleCashParamDiv;
|
||||
powerData[opponentItemData.playFabID] = opponentItemData.power;
|
||||
//opponentItemDatasPre.Add(opponentItemData);
|
||||
opponentNotFriendItemDatasPre.Add(opponentItemData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int GetConstructionID(int level)
|
||||
{
|
||||
List<Construction> _dataList = _tables.TbConstruction.DataList;
|
||||
int id = _dataList[0].ID;
|
||||
for (int j = _dataList.Count - 1; j >= 0; j--)
|
||||
{
|
||||
if (_dataList[j].InitialLevel <= level)
|
||||
{
|
||||
id = _dataList[j].ID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
void GetFriendData()
|
||||
{
|
||||
var FriendList = GContext.container.Resolve<FriendService>().FriendList;
|
||||
opponentFriendItemDatasPre.Clear();
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow().Date.AddDays(-1);
|
||||
if (FriendList != null && FriendList.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < FriendList.Count; i++)
|
||||
{
|
||||
if (FriendList[i].playFabId == userService.UserId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
OpponentItemData opponentItemData = new OpponentItemData();
|
||||
opponentItemData.playFabID = FriendList[i].playFabId;
|
||||
opponentItemData.target_source = 1;
|
||||
|
||||
opponentFriendItemDatasPre.Add(opponentItemData);
|
||||
int level = FriendList[i].value / LeadboardData.LV_MODELING;
|
||||
|
||||
opponentItemData.BombID = GetConstructionID(level);
|
||||
opponentItemData.power = (float)Math.Log10(BottleCashParamBase + level / BottleCashParamLevel) / BottleCashParamDiv;
|
||||
powerData[opponentItemData.playFabID] = opponentItemData.power;
|
||||
//opponentItemDatasPre.Add(opponentItemData);
|
||||
opponentFriend.Add(opponentItemData);
|
||||
opponentFriendDic[opponentItemData.playFabID] = opponentItemData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async void SetEnemy(EnemyType enemyType, long glod)
|
||||
{
|
||||
if (!opponentData.isRobot && opponentData.playFabID.Length > 10)
|
||||
{
|
||||
SetEnemyIDRequest setEnemyCampIDRequest = new SetEnemyIDRequest()
|
||||
{
|
||||
enemyType = enemyType,
|
||||
playerID = userService.UserId,
|
||||
enemyID = opponentData.playFabID,
|
||||
content = glod.ToString()
|
||||
};
|
||||
var customServerMgr = GContext.container.Resolve<ICustomServerMgr>();
|
||||
string json = await customServerMgr.BombRequest("SetEnemyID", setEnemyCampIDRequest);
|
||||
if (!string.IsNullOrEmpty(json) && json != "[]")
|
||||
{
|
||||
SetEnemyIDResponse setCampIDResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<SetEnemyIDResponse>(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
//向服务器请求仇人列表
|
||||
async Task GetEnemyData(EnemyType enemyType)
|
||||
{
|
||||
GetEnemyIDRequest getEnemyCampIDRequest = new GetEnemyIDRequest()
|
||||
{
|
||||
playerID = userService.UserId,
|
||||
enemyType = enemyType,
|
||||
};
|
||||
var customServerMgr = GContext.container.Resolve<ICustomServerMgr>();
|
||||
string json = await customServerMgr.BombRequest("GetEnemyID", getEnemyCampIDRequest);
|
||||
if (!string.IsNullOrEmpty(json) && json != "[]")
|
||||
{
|
||||
GetEnemyIDResponse getCampIDResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<GetEnemyIDResponse>(json);
|
||||
if (getCampIDResponse.enemyIDs == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
List<EventPopupData> eventPopupDatas = GContext.container.Resolve<ClubService>().eventPopupDatas;
|
||||
//DateTime dateTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
//EventPopupData eventPopupData;
|
||||
int count;
|
||||
ReportDataType reportDataType = ReportDataType.Bomb;
|
||||
switch (enemyType)
|
||||
{
|
||||
case EnemyType.Bomb:
|
||||
reportDataType = ReportDataType.Bomb;
|
||||
break;
|
||||
case EnemyType.Heist:
|
||||
reportDataType = ReportDataType.Heist;
|
||||
break;
|
||||
case EnemyType.Aquarium:
|
||||
reportDataType = ReportDataType.Aquarium;
|
||||
break;
|
||||
}
|
||||
List<OpponentItemData> opponentItemDatas = new List<OpponentItemData>();
|
||||
opponentEnemy[enemyType] = opponentItemDatas;
|
||||
eventPopupDatas = eventPopupDatas.Where(x => (int)(x.type) == (int)reportDataType).ToList();
|
||||
for (int i = 0; i < getCampIDResponse.enemyIDs.Count; i++)
|
||||
{
|
||||
if (getCampIDResponse.enemyIDs[i] == userService.UserId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
OpponentItemData opponentItemData = new OpponentItemData();
|
||||
opponentItemData.playFabID = getCampIDResponse.enemyIDs[i];
|
||||
|
||||
if (getCampIDResponse.levels != null)
|
||||
{
|
||||
if (getCampIDResponse.levels.TryGetValue(opponentItemData.playFabID, out int level))
|
||||
{
|
||||
opponentItemData.BombID = GetConstructionID(level);
|
||||
}
|
||||
}
|
||||
|
||||
if (powerData.ContainsKey(opponentItemData.playFabID))
|
||||
{
|
||||
opponentItemData.power = powerData[opponentItemData.playFabID];
|
||||
}
|
||||
else
|
||||
{
|
||||
opponentItemData.power = 1;
|
||||
}
|
||||
count = eventPopupDatas.Where(x => x.playerID == opponentItemData.playFabID).Count();
|
||||
if (reportDataType == ReportDataType.Aquarium)
|
||||
{
|
||||
if (count > 1)
|
||||
{
|
||||
opponentItemData.info = LocalizationMgr.GetFormatTextValue("UI_EventBankHeistOpponentPopupPanel_3", count);
|
||||
}
|
||||
else
|
||||
{
|
||||
opponentItemData.info = LocalizationMgr.GetFormatTextValue("UI_EventBankHeistOpponentPopupPanel_5", 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (count > 1)
|
||||
{
|
||||
opponentItemData.info = LocalizationMgr.GetFormatTextValue("UI_EventBankHeistOpponentPopupPanel_6", count);
|
||||
}
|
||||
else
|
||||
{
|
||||
opponentItemData.info = LocalizationMgr.GetFormatTextValue("UI_EventBankHeistOpponentPopupPanel_7", 1);
|
||||
}
|
||||
}
|
||||
if (opponentFriendDic.TryGetValue(opponentItemData.playFabID, out OpponentItemData friendData))
|
||||
{
|
||||
friendData.info = opponentItemData.info;
|
||||
}
|
||||
opponentItemData.target_source = 4;
|
||||
|
||||
opponentItemDatas.Add(opponentItemData);
|
||||
//opponentNotFriendItemDatasPre.Add(opponentItemData);
|
||||
}
|
||||
}
|
||||
}
|
||||
void GetRobotData()
|
||||
{
|
||||
int count = opponentItemDatasPre.Count;
|
||||
int allCount = 20;
|
||||
if (count >= 20)
|
||||
{
|
||||
allCount = count + 1;
|
||||
}
|
||||
var robots = new List<cfg.Robot>(_tables.TbRobot.DataList);
|
||||
cfg.Robot robot;
|
||||
int myID = campData.Id;
|
||||
cfg.TbConstruction tbConstruction = _tables.TbConstruction;
|
||||
int constructionFirstId = tbConstruction.DataList[0].ID % 1000;
|
||||
int constructionLastId = tbConstruction.DataList[^1].ID;
|
||||
int campId;
|
||||
for (int i = count; i < allCount; i++)
|
||||
{
|
||||
robot = robots[UnityEngine.Random.Range(0, robots.Count)];
|
||||
robots.Remove(robot);
|
||||
OpponentItemData opponentItemData = new OpponentItemData();
|
||||
opponentItemData.playFabID = robot.ID.ToString("X");
|
||||
opponentItemData.target_source = -1;
|
||||
userService.SetPlayInfo(opponentItemData.playFabID, LocalizationMgr.GetText(robot.Name_l10n_key), robot.Avatar);
|
||||
//opponentItemData.playName = robot.Name;
|
||||
//opponentItemData.url = robot.Avatar;
|
||||
opponentItemData.isRobot = true;
|
||||
|
||||
campId = robot.ConstructionOffset + myID;
|
||||
if (campId < constructionFirstId)
|
||||
{
|
||||
campId = constructionFirstId;
|
||||
}
|
||||
if (campId > constructionLastId)
|
||||
{
|
||||
campId = constructionLastId;
|
||||
}
|
||||
opponentItemData.BombID = campId;
|
||||
opponentItemData.power = (float)Math.Log10(BottleCashParamBase - i) / BottleCashParamDiv;
|
||||
opponentItemDatasPre.Add(opponentItemData);
|
||||
}
|
||||
}
|
||||
async Task GetLeaderboardData(EnemyType enemyType)
|
||||
{
|
||||
FishingEventData _fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
if (!_fishingEventData.IsOpenRank())
|
||||
{
|
||||
return;
|
||||
}
|
||||
LeadboardData leadboardData = GContext.container.Resolve<LeadboardData>();
|
||||
if (leadboardData.curEventRank == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Dictionary<int, cfg.Robot> robots = GContext.container.Resolve<cfg.Tables>().TbRobot.DataMap;
|
||||
GetLeaderboardsResp curEventRank = leadboardData.curEventRank;
|
||||
int myID = campData.Id;
|
||||
cfg.TbConstruction tbConstruction = _tables.TbConstruction;
|
||||
int constructionFirstId = tbConstruction.DataList[0].ID % 1000;
|
||||
int constructionLastId = tbConstruction.DataList[^1].ID;
|
||||
int campId = myID;
|
||||
Dictionary<string, OpponentItemData> playerList = new Dictionary<string, OpponentItemData>();
|
||||
for (int i = 0; i < curEventRank.data.Length; i++)
|
||||
{
|
||||
if (curEventRank.data[i].isMe || curEventRank.data[i].id == userService.UserId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (curEventRank.data[i].id == null || powerData.ContainsKey(curEventRank.data[i].id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
OpponentItemData opponentItemData = new OpponentItemData();
|
||||
opponentItemData.playFabID = curEventRank.data[i].id;
|
||||
if (curEventRank.data[i].isBot)
|
||||
{
|
||||
int b = Convert.ToInt32(opponentItemData.playFabID, 16);
|
||||
if (robots.TryGetValue(b, out cfg.Robot robot))
|
||||
{
|
||||
campId = robot.ConstructionOffset + myID;
|
||||
}
|
||||
if (campId < constructionFirstId)
|
||||
{
|
||||
campId = constructionFirstId;
|
||||
}
|
||||
if (campId > constructionLastId)
|
||||
{
|
||||
campId = constructionLastId;
|
||||
}
|
||||
opponentItemData.isRobot = true;
|
||||
opponentItemData.target_source = -1;
|
||||
opponentItemDatasPre.Add(opponentItemData);
|
||||
}
|
||||
else
|
||||
{
|
||||
playerList[opponentItemData.playFabID] = opponentItemData;
|
||||
opponentItemData.target_source = 3;
|
||||
userService.GetPlayInfo(opponentItemData.playFabID);
|
||||
opponentNotFriendItemDatasPre.Add(opponentItemData);
|
||||
}
|
||||
opponentItemData.BombID = campId;
|
||||
opponentItemData.power = (float)Math.Log10(BottleCashParamBase - curEventRank.data[i].rank) / BottleCashParamDiv;
|
||||
powerData[opponentItemData.playFabID] = opponentItemData.power;
|
||||
}
|
||||
if (playerList.Count > 0 /*&& enemyType == EnemyType.Bomb*/)
|
||||
{
|
||||
await GetCampID(playerList);
|
||||
}
|
||||
}
|
||||
async Task GetCampID(Dictionary<string, OpponentItemData> playerList)
|
||||
{
|
||||
List<string> playFabIds = playerList.Values.Select(x => x.playFabID).ToList();
|
||||
|
||||
GetCampIDRequest getCampIDRequest = new GetCampIDRequest()
|
||||
{
|
||||
playerIDs = playFabIds
|
||||
};
|
||||
var customServerMgr = GContext.container.Resolve<ICustomServerMgr>();
|
||||
string json = await customServerMgr.BombRequest("GetCampID", getCampIDRequest);
|
||||
if (!string.IsNullOrEmpty(json) && json != "[]")
|
||||
{
|
||||
GetCampIDResponse getCampIDResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<GetCampIDResponse>(json);
|
||||
if (getCampIDResponse != null && getCampIDResponse.levels != null && getCampIDResponse.levels.Count > 0)
|
||||
{
|
||||
foreach (var item in getCampIDResponse.levels)
|
||||
{
|
||||
playerList[item.Key].BombID = GetConstructionID(item.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,657 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using LitJson;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public class TriggerPackBuyData
|
||||
{
|
||||
public int ID;
|
||||
//领取第几个
|
||||
public int index;
|
||||
//礼包类型
|
||||
public int lastID;
|
||||
//结束时间
|
||||
public string time;
|
||||
//触发当时VIP等级
|
||||
public int vipLevel;
|
||||
//触发当时地图ID
|
||||
public int mapID;
|
||||
}
|
||||
public class GiftVIPDData
|
||||
{
|
||||
public int oldVipLevel;
|
||||
public int curVipLevel;
|
||||
//记录数据用于明天时间(天)
|
||||
public int timer;
|
||||
}
|
||||
public class TriggerPackEvent
|
||||
{
|
||||
|
||||
}
|
||||
public class TriggerPackData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
Tables _tables;
|
||||
PlayerShopData playerShopData;
|
||||
public TriggerPackData(Tables tables)
|
||||
{
|
||||
_tables = tables;
|
||||
}
|
||||
//固定时机开启的过期时间
|
||||
Dictionary<int, FixedTimeEvent> fixedTimeShopDic = new Dictionary<int, FixedTimeEvent>();
|
||||
|
||||
//触发礼包的数据
|
||||
public List<TriggerPackBuyData> triggerPackList = new List<TriggerPackBuyData>();
|
||||
|
||||
//VIP 等级叠加
|
||||
public Dictionary<string, GiftVIPDData> giftVIPDData = new Dictionary<string, GiftVIPDData>();
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVTriggerState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) => game.PlayerDataStorage.PlayerKVSaveSchema.DecodeTriggers(raw,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
triggerPackList=new List<TriggerPackBuyData>();
|
||||
giftVIPDData=new Dictionary<string,GiftVIPDData>(StringComparer.Ordinal);
|
||||
fixedTimeShopDic=new Dictionary<int,FixedTimeEvent>();
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVTriggerState state)
|
||||
{
|
||||
if(state==null) throw new ArgumentNullException(nameof(state));
|
||||
var packs=state.Packs.Select(p=>new TriggerPackBuyData {
|
||||
ID=p.ID,index=p.index,lastID=p.lastID,vipLevel=p.vipLevel,mapID=p.mapID,time=p.time }).ToList();
|
||||
var gifts=state.Gifts.ToDictionary(p=>p.Key,p=>new GiftVIPDData {
|
||||
oldVipLevel=p.Value.oldVipLevel,curVipLevel=p.Value.curVipLevel,timer=p.Value.timer },StringComparer.Ordinal);
|
||||
var fixedValues=state.Fixed.ToDictionary(p=>p.Key,p=>new FixedTimeEvent { id=p.Value.id,endTime=p.Value.endTime });
|
||||
triggerPackList=packs; giftVIPDData=gifts; fixedTimeShopDic=fixedValues;
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
internal FixedTimeEvent FixedForKVValidation(int key) => fixedTimeShopDic.TryGetValue(key,out var value) ? value : null;
|
||||
internal bool IsEmptyForKVValidation => triggerPackList.Count==0 && giftVIPDData.Count==0 && fixedTimeShopDic.Count==0;
|
||||
#endif
|
||||
public void Init()
|
||||
{
|
||||
playerShopData = GContext.container.Resolve<PlayerShopData>();
|
||||
GContext.OnEvent<ConditionTypeEvent>().Subscribe(UpdateTriggerPackData);
|
||||
InitPackData();
|
||||
}
|
||||
#region 礼包限制、开启、初始化
|
||||
public void SetFixedTimeShopDic(Dictionary<int, FixedTimeEvent> _limitedTimeShopDic)
|
||||
{
|
||||
fixedTimeShopDic = _limitedTimeShopDic;
|
||||
}
|
||||
public bool FixedTimeEventExpired(int eventId)
|
||||
{
|
||||
if (fixedTimeShopDic.TryGetValue(eventId, out FixedTimeEvent fixedTimeEvent))
|
||||
{
|
||||
var endTime = GlobalUtils.TryParseDateTime(fixedTimeEvent.endTime, ZZTimeHelper.UtcNow().UtcNowOffset());
|
||||
return endTime.Ticks > ZZTimeHelper.UtcNow().UtcNowOffset().Ticks;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public void InitPackData()
|
||||
{
|
||||
InitGiftVIPDData();
|
||||
//过期礼包删除
|
||||
DateTime curTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
for (int i = triggerPackList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
TriggerPackBuyData item = triggerPackList[i];
|
||||
DateTime endTime = GlobalUtils.TryParseDateTime(item.time, curTime);
|
||||
if (endTime.Ticks <= curTime.Ticks)
|
||||
{
|
||||
triggerPackList.Remove(item);
|
||||
}
|
||||
}
|
||||
List<TriggerPackManager> _dataList = _tables.TbTriggerPackManager.DataList;
|
||||
foreach (var t in _dataList)
|
||||
{
|
||||
if (t.PackType == 2)
|
||||
{
|
||||
ConditionTypeAdd(t);
|
||||
}
|
||||
}
|
||||
triggerPackList.Sort((a, b) => a.lastID.CompareTo(b.lastID));
|
||||
}
|
||||
bool isInitFace;
|
||||
public void SetInitFace()
|
||||
{
|
||||
if (isInitFace)
|
||||
{
|
||||
return;
|
||||
}
|
||||
isInitFace = true;
|
||||
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (playerShopData.GetShopPackBuyCount(triggerPackList[i].lastID) == 0 && triggerPackList[i].lastID != 4)
|
||||
{
|
||||
playerShopData.AddShopPackDailyBuyCount(triggerPackList[i].lastID, 1, false);
|
||||
}
|
||||
if (triggerPackList[i].mapID <= 0)
|
||||
{
|
||||
triggerPackList[i].mapID = GContext.container.Resolve<PlayerData>().currentMapId;
|
||||
}
|
||||
if (triggerPackList[i].lastID == 53)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(triggerPackList[i].ID, UITypes.MapPackPanel, triggerPackList[i].lastID);
|
||||
}
|
||||
else if (triggerPackList[i].lastID == 2)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(triggerPackList[i].ID, UITypes.CommerceNoviceGiftPopupPanel, triggerPackList[i].lastID);
|
||||
}
|
||||
else if (triggerPackList[i].lastID == 1)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(triggerPackList[i].ID, UITypes.GiftPopupPanel_6, triggerPackList[i].lastID);
|
||||
}
|
||||
else if (triggerPackList[i].lastID == 5)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(triggerPackList[i].ID, UITypes.AdsPackPanel, triggerPackList[i].lastID);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public void UpdateTriggerPackData(ConditionTypeEvent typeData)
|
||||
{
|
||||
ConditionType type = typeData.type;
|
||||
List<TriggerPackManager> _dataList = _tables.TbTriggerPackManager.DataList;
|
||||
foreach (var t in _dataList)
|
||||
{
|
||||
if (t.Condition == type)
|
||||
{
|
||||
if (ConditionTypeAdd(t, true, typeData.count))
|
||||
{
|
||||
if (t.PackType == 53)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(t.ID, UITypes.MapPackPanel, t.PackType);
|
||||
GContext.Publish(new TriggerPackEvent());
|
||||
}
|
||||
else if (t.PackType == 2)
|
||||
{
|
||||
//SetPack2Panel(t);
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(t.ID, UITypes.CommerceNoviceGiftPopupPanel, t.PackType);
|
||||
GContext.Publish(new TriggerPackEvent());
|
||||
}
|
||||
else if (t.PackType == 1)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(t.ID, UITypes.GiftPopupPanel_6, t.PackType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 钓鱼失败 看完广告 触发礼包
|
||||
/// </summary>
|
||||
public void TriggerAdsPack()
|
||||
{
|
||||
if (!playerShopData.IsOpenAdPack() || playerShopData.GetAdticketCount() >= 5 ||
|
||||
GContext.container.Resolve<PlayerData>().vip < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TriggerPackManager t = _tables.TbTriggerPackManager.GetOrDefault(4000101);
|
||||
if (t == null || t.PackType != 5)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//特殊的 广告礼包 使用 fixedTimeShopDic 记录CD时间
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
|
||||
if (!FixedTimeEventExpired(t.ID))
|
||||
{
|
||||
fixedTimeShopDic.Remove(t.ID);
|
||||
}
|
||||
if (!fixedTimeShopDic.ContainsKey(t.ID))
|
||||
{
|
||||
FixedTime fixedTime = (FixedTime)t.TimeDefinition;
|
||||
//广告礼包记录的是 cd时间
|
||||
fixedTimeShopDic[t.ID] = new FixedTimeEvent()
|
||||
{
|
||||
id = t.ID,
|
||||
endTime = dateTime.AddSeconds(fixedTime.EndTime).ToString(),
|
||||
};
|
||||
SaveStore.Write("FixedTimeShopDic", JsonMapper.ToJson(fixedTimeShopDic)).RequireAccepted();
|
||||
|
||||
TriggerPackBuyData _newSignEvent = null;
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (triggerPackList[i].ID == t.ID)
|
||||
{
|
||||
_newSignEvent = triggerPackList[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
string time = dateTime.AddSeconds(fixedTime.StartTime).ToString();
|
||||
if (_newSignEvent == null)
|
||||
{
|
||||
_newSignEvent = new TriggerPackBuyData()
|
||||
{
|
||||
ID = t.ID,
|
||||
lastID = t.PackType,
|
||||
index = 0,
|
||||
time = time,
|
||||
vipLevel = GetVIPLevel(t.PackType),
|
||||
mapID = GContext.container.Resolve<PlayerData>().lastMapId,
|
||||
};
|
||||
triggerPackList.Insert(0, _newSignEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
_newSignEvent.time = time;
|
||||
_newSignEvent.vipLevel = GetVIPLevel(t.PackType);
|
||||
}
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(_newSignEvent.ID, UITypes.AdsPackPanel, _newSignEvent.lastID);
|
||||
SaveStore.Write("TriggerPackDataList", JsonMapper.ToJson(triggerPackList)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshTriggerPackData(int id, int mapID)
|
||||
{
|
||||
TriggerPackManager t = _tables.TbTriggerPackManager.GetOrDefault(id);
|
||||
bool isOpen = playerShopData.GetShopPackBuyCount(t.PackType) < t.MaxTrigger;
|
||||
|
||||
if (isOpen)
|
||||
{
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
TriggerPackBuyData _newSignEvent = null;
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (triggerPackList[i].ID == t.ID)
|
||||
{
|
||||
_newSignEvent = triggerPackList[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_newSignEvent != null)
|
||||
{
|
||||
triggerPackList.Remove(_newSignEvent);
|
||||
}
|
||||
string time = dateTime.AddDays(10).ToString();
|
||||
if (t.TimeDefinition is FixedTime)
|
||||
{
|
||||
FixedTime fixedTime = (FixedTime)t.TimeDefinition;
|
||||
time = ZZTimeHelper.UtcNow().UtcNowOffset().AddSeconds(fixedTime.StartTime).ToString();
|
||||
FixedTimeEvent fixedTimeEvent = new FixedTimeEvent()
|
||||
{
|
||||
id = t.ID,
|
||||
endTime = time,
|
||||
};
|
||||
fixedTimeShopDic[t.ID] = fixedTimeEvent;
|
||||
SaveStore.Write("FixedTimeShopDic", JsonMapper.ToJson(fixedTimeShopDic)).RequireAccepted();
|
||||
}
|
||||
else if (t.TimeDefinition is LimitedTime)
|
||||
{
|
||||
LimitedTime limitedTime = (LimitedTime)t.TimeDefinition;
|
||||
time = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow()).UtcNowOffset().ToString();
|
||||
}
|
||||
|
||||
_newSignEvent = new TriggerPackBuyData()
|
||||
{
|
||||
ID = t.ID,
|
||||
lastID = t.PackType,
|
||||
index = 0,
|
||||
time = time,
|
||||
vipLevel = GetVIPLevel(t.PackType),
|
||||
mapID = mapID,
|
||||
};
|
||||
triggerPackList.Insert(0, _newSignEvent);
|
||||
playerShopData.AddShopPackDailyBuyCount(t.PackType, 1, false);
|
||||
SaveStore.Write("TriggerPackDataList", JsonMapper.ToJson(triggerPackList)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 特殊触发礼包
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
public void AddTriggerPackData(int id)
|
||||
{
|
||||
TriggerPackManager t = _tables.TbTriggerPackManager.GetOrDefault(id);
|
||||
|
||||
bool isOpen = t.MaxTrigger == 0 || ((t.PackType == 1 || t.PackType == 2) && GContext.container.Resolve<PlayerShopData>().GetAllPackCount(t.ID) < t.MaxTrigger)//类型为1.2新手礼包
|
||||
|| (t.PackType != 1 && t.PackType != 2 && playerShopData.GetShopPackBuyCount(t.PackType) < t.MaxTrigger);//每日限购礼包
|
||||
|
||||
if (isOpen)
|
||||
{
|
||||
if (t.TimeDefinition is FixedTime)
|
||||
{
|
||||
if (t.PackType == 53)
|
||||
{
|
||||
fixedTimeShopDic.Remove(t.ID);
|
||||
}
|
||||
if (!fixedTimeShopDic.ContainsKey(t.ID))
|
||||
{
|
||||
FixedTime fixedTime = (FixedTime)t.TimeDefinition;
|
||||
fixedTimeShopDic[t.ID] = new FixedTimeEvent()
|
||||
{
|
||||
id = t.ID,
|
||||
endTime = ZZTimeHelper.UtcNow().UtcNowOffset().AddSeconds(fixedTime.StartTime).ToString(),
|
||||
};
|
||||
|
||||
SaveStore.Write("FixedTimeShopDic", JsonMapper.ToJson(fixedTimeShopDic)).RequireAccepted();
|
||||
}
|
||||
if (FixedTimeEventExpired(t.ID))
|
||||
{
|
||||
AddTriggerPack(t);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddTriggerPack(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool ConditionTypeAdd(TriggerPackManager t, bool trigger = false, int Param = 0)
|
||||
{
|
||||
bool isOpen = false;
|
||||
if (Condition(t, false, Param))
|
||||
{
|
||||
if (t.TimeDefinition is FixedTime)
|
||||
{
|
||||
if (trigger && (t.PackType == 53))
|
||||
{
|
||||
fixedTimeShopDic.Remove(t.ID);
|
||||
}
|
||||
if (!fixedTimeShopDic.ContainsKey(t.ID))
|
||||
{
|
||||
FixedTime fixedTime = (FixedTime)t.TimeDefinition;
|
||||
fixedTimeShopDic[t.ID] = new FixedTimeEvent()
|
||||
{
|
||||
id = t.ID,
|
||||
endTime = ZZTimeHelper.UtcNow().UtcNowOffset().AddSeconds(fixedTime.StartTime).ToString(),
|
||||
};
|
||||
|
||||
SaveStore.Write("FixedTimeShopDic", JsonMapper.ToJson(fixedTimeShopDic)).RequireAccepted();
|
||||
}
|
||||
if (FixedTimeEventExpired(t.ID))
|
||||
{
|
||||
AddTriggerPack(t);
|
||||
isOpen = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddTriggerPack(t);
|
||||
isOpen = true;
|
||||
}
|
||||
}
|
||||
return isOpen;
|
||||
}
|
||||
bool Condition(TriggerPackManager packManager, bool isAdd, int Param)
|
||||
{
|
||||
if (packManager.PackType == 5)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool isOpen = packManager.MaxTrigger == 0 || ((packManager.PackType == 1 || packManager.PackType == 2) && GContext.container.Resolve<PlayerShopData>().GetAllPackCount(packManager.ID) < packManager.MaxTrigger)//类型为1.2新手礼包
|
||||
|| (packManager.PackType != 1 && packManager.PackType != 2 && playerShopData.GetShopPackBuyCount(packManager.PackType) < packManager.MaxTrigger);//每日限购礼包
|
||||
if (!isOpen)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packManager.TimeDefinition is LimitedTime)
|
||||
{
|
||||
LimitedTime limitedTime = (LimitedTime)packManager.TimeDefinition;
|
||||
var timer = ZZTimeHelper.UtcNow();
|
||||
isOpen = GlobalUtils.TryParseDateTime(limitedTime.StartTime, timer) <= timer && timer < GlobalUtils.TryParseDateTime(limitedTime.EndTime, timer);
|
||||
}
|
||||
|
||||
switch (packManager.Condition)
|
||||
{
|
||||
case ConditionType.AccountLevel:
|
||||
if (isAdd)
|
||||
{
|
||||
isOpen &= GContext.container.Resolve<PlayerData>().lv >= int.Parse(packManager.Param[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
isOpen &= GContext.container.Resolve<PlayerData>().lv == int.Parse(packManager.Param[0]);
|
||||
}
|
||||
break;
|
||||
case ConditionType.GetFishCount:
|
||||
if (isAdd)
|
||||
{
|
||||
isOpen &= GContext.container.Resolve<PlayerFishData>().GetAnglingCount() >= int.Parse(packManager.Param[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
isOpen &= GContext.container.Resolve<PlayerFishData>().GetAnglingCount() == int.Parse(packManager.Param[0]);
|
||||
}
|
||||
break;
|
||||
case ConditionType.CompleteGuide:
|
||||
isOpen &= GContext.container.Resolve<GuideDataCenter>().GetIsFinish(packManager.Param[0]);
|
||||
break;
|
||||
case ConditionType.BuyPack:
|
||||
isOpen &= playerShopData.shopAllPackBuyCount.GetValueOrDefault(int.Parse(packManager.Param[0])) > 0;
|
||||
break;
|
||||
case ConditionType.MapUnlocked:
|
||||
//isOpen &= GContext.container.Resolve<PlayerData>().MapIsUnlock(packManager.Param);
|
||||
isOpen &= packManager.Param.Contains(Param.ToString());
|
||||
break;
|
||||
}
|
||||
// if ( packManager.PackType == 2 )
|
||||
// Debug.Log("??" + packManager.Condition+GContext.container.Resolve<PlayerData>().lv+int.Parse(packManager.Param[0])+ (GContext.container.Resolve<PlayerData>().lv >= int.Parse(packManager.Param[0])) + " " +isOpen);
|
||||
return isOpen;
|
||||
}
|
||||
#endregion
|
||||
void InitGiftVIPDData()
|
||||
{
|
||||
if (giftVIPDData.Count > 0)
|
||||
{
|
||||
var timer = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
int dateTime = timer.AddDays(1).DayOfYear;
|
||||
bool isUpdate = false;
|
||||
foreach (var item in giftVIPDData)
|
||||
{
|
||||
GiftVIPDData giftVIPDData = item.Value;
|
||||
if (giftVIPDData.timer != dateTime)
|
||||
{
|
||||
isUpdate = true;
|
||||
if (giftVIPDData.timer == timer.DayOfYear)
|
||||
{
|
||||
giftVIPDData.oldVipLevel = giftVIPDData.curVipLevel;
|
||||
giftVIPDData.curVipLevel = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
giftVIPDData.oldVipLevel = 0;
|
||||
giftVIPDData.curVipLevel = 0;
|
||||
}
|
||||
giftVIPDData.timer = dateTime;
|
||||
}
|
||||
}
|
||||
if (isUpdate)
|
||||
{
|
||||
SaveStore.Write("GiftVIPDData", JsonMapper.ToJson(giftVIPDData)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
}
|
||||
public int GetVIPLevel(int packType)
|
||||
{
|
||||
string PackType = packType.ToString();
|
||||
return GetVIPLevel(PackType);
|
||||
}
|
||||
public void SetVIPLevel(int packType, int VIPBonusForNextType)
|
||||
{
|
||||
string PackType = packType.ToString();
|
||||
SetVIPLevel(PackType, VIPBonusForNextType);
|
||||
}
|
||||
public int GetVIPLevel(string type)
|
||||
{
|
||||
int vipLevel = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
if (giftVIPDData.TryGetValue(type, out GiftVIPDData giftVIPD))
|
||||
{
|
||||
vipLevel += giftVIPD.oldVipLevel;
|
||||
}
|
||||
if (vipLevel >= _tables.TbVipLevel.DataList.Count)
|
||||
{
|
||||
vipLevel = _tables.TbVipLevel.DataList.Count - 1;
|
||||
}
|
||||
return vipLevel;
|
||||
}
|
||||
public void SetVIPLevel(string PackType, int VIPBonusForNextType)
|
||||
{
|
||||
if (VIPBonusForNextType > 0)
|
||||
{
|
||||
if (giftVIPDData.TryGetValue(PackType, out GiftVIPDData giftVIPD))
|
||||
{
|
||||
giftVIPD.curVipLevel += VIPBonusForNextType;
|
||||
}
|
||||
else
|
||||
{
|
||||
giftVIPD = new GiftVIPDData()
|
||||
{
|
||||
curVipLevel = VIPBonusForNextType,
|
||||
timer = ZZTimeHelper.UtcNow().UtcNowOffset().AddDays(1).DayOfYear,
|
||||
};
|
||||
}
|
||||
giftVIPDData[PackType] = giftVIPD;
|
||||
SaveStore.Write("GiftVIPDData", JsonMapper.ToJson(giftVIPDData)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
#region 触发礼包
|
||||
public void AddTriggerPack(TriggerPackManager t)
|
||||
{
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
TriggerPackBuyData _newSignEvent = null;
|
||||
//if (t.PackType != 53)
|
||||
//{
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (triggerPackList[i].ID == t.ID)
|
||||
{
|
||||
_newSignEvent = triggerPackList[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
//}
|
||||
if (_newSignEvent != null &&
|
||||
(GlobalUtils.TryParseDateTime(_newSignEvent.time, ZZTimeHelper.UtcNow()).Ticks < dateTime.Ticks))
|
||||
{
|
||||
triggerPackList.Remove(_newSignEvent);
|
||||
_newSignEvent = null;
|
||||
}
|
||||
if (_newSignEvent == null)
|
||||
{
|
||||
string time = dateTime.AddDays(10).ToString();
|
||||
if (t.TimeDefinition is FixedTime)
|
||||
{
|
||||
if (fixedTimeShopDic.TryGetValue(t.ID, out FixedTimeEvent fixedTimeEvent))
|
||||
{
|
||||
time = fixedTimeEvent.endTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
FixedTime fixedTime = (FixedTime)t.TimeDefinition;
|
||||
time = dateTime.AddSeconds(fixedTime.StartTime).ToString();
|
||||
}
|
||||
}
|
||||
else if (t.TimeDefinition is LimitedTime)
|
||||
{
|
||||
LimitedTime limitedTime = (LimitedTime)t.TimeDefinition;
|
||||
time = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow()).UtcNowOffset().ToString();
|
||||
}
|
||||
|
||||
_newSignEvent = new TriggerPackBuyData()
|
||||
{
|
||||
ID = t.ID,
|
||||
lastID = t.PackType,
|
||||
index = 0,
|
||||
time = time,
|
||||
vipLevel = GetVIPLevel(t.PackType),
|
||||
mapID = GContext.container.Resolve<PlayerData>().lastMapId,
|
||||
};
|
||||
triggerPackList.Insert(0, _newSignEvent);
|
||||
playerShopData.AddShopPackDailyBuyCount(t.PackType, 1, false);
|
||||
SaveStore.Write("TriggerPackDataList", JsonMapper.ToJson(triggerPackList)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
public void AddTriggerPackCount(int id)
|
||||
{
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (triggerPackList[i].ID == id)
|
||||
{
|
||||
if (++triggerPackList[i].index >= _tables.TbTriggerPackManager.Get(triggerPackList[i].ID).VIPPackList[GetVIPLevel(triggerPackList[i].lastID)].Count)
|
||||
{
|
||||
triggerPackList.Remove(triggerPackList[i]);
|
||||
}
|
||||
SaveStore.Write("TriggerPackDataList", JsonMapper.ToJson(triggerPackList)).RequireAccepted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
playerShopData.AddAllPackCount(id, 1);
|
||||
TriggerPackManager packManager = _tables.TbTriggerPackManager.GetOrDefault(id);
|
||||
GContext.Publish(new TriggerPackEvent());
|
||||
if (packManager.VIPBonus > 0)
|
||||
{
|
||||
SetVIPLevel(packManager.PackType, packManager.VIPBonus);
|
||||
}
|
||||
}
|
||||
public List<TriggerPackBuyData> GetTriggerPackList(int type)
|
||||
{
|
||||
List<TriggerPackBuyData> newTriggerPackList = new List<TriggerPackBuyData>();
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (triggerPackList[i].lastID == type && _tables.TbTriggerPackManager.GetOrDefault(triggerPackList[i].ID) != null)
|
||||
{
|
||||
newTriggerPackList.Add(triggerPackList[i]);
|
||||
}
|
||||
}
|
||||
newTriggerPackList.Sort((a, b) => a.ID.CompareTo(b.ID));
|
||||
return newTriggerPackList;
|
||||
}
|
||||
public void ShowTriggerPack(int type, int id)
|
||||
{
|
||||
for (int i = 0; i < triggerPackList.Count; i++)
|
||||
{
|
||||
if (triggerPackList[i].lastID == type && triggerPackList[i].ID == id)
|
||||
{
|
||||
var t = _tables.TbTriggerPackManager.GetOrDefault(id);
|
||||
if (t.PackType == 53)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(t.ID, UITypes.MapPackPanel, t.PackType);
|
||||
}
|
||||
else if (t.PackType == 2)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(t.ID, UITypes.CommerceNoviceGiftPopupPanel, t.PackType);
|
||||
}
|
||||
else if (t.PackType == 1)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIOnLogin(t.ID, UITypes.GiftPopupPanel_6, t.PackType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public TimeSpan GetCurTriggerPackEndTime(TriggerPackBuyData dailySignEvent)
|
||||
{
|
||||
DateTime curTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
DateTime endTime = GlobalUtils.TryParseDateTime(dailySignEvent.time, curTime);
|
||||
if (endTime.Ticks <= curTime.Ticks)
|
||||
{
|
||||
triggerPackList.Remove(dailySignEvent);
|
||||
GContext.Publish(new TriggerPackEvent());
|
||||
SaveStore.Write("TriggerPackDataList", JsonMapper.ToJson(triggerPackList)).RequireAccepted();
|
||||
}
|
||||
return endTime - curTime;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public TriggerPackBuyData GetTriggerPackBuyDataById(int packId)
|
||||
{
|
||||
return triggerPackList.FirstOrDefault(x => x.ID == packId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using asap.core;
|
||||
using GameCore;
|
||||
using cfg;
|
||||
public class EventPackData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
private Tables _tables = GContext.container.Resolve<Tables>();
|
||||
private struct Data
|
||||
{
|
||||
public int currentEventID;
|
||||
public int purchaseCount;
|
||||
public int VIPLvlWhenEventActivate;
|
||||
public int redirectID;
|
||||
public Data(int eventID = 0, int p = 0, int v = 0, int rid = 0)
|
||||
{
|
||||
currentEventID = eventID;
|
||||
purchaseCount = p;
|
||||
VIPLvlWhenEventActivate = v;
|
||||
redirectID = rid;
|
||||
}
|
||||
}
|
||||
private Data _data;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVEventPackState DecodeForKVLoad(System.Collections.Generic.IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeEventPack(raw,snapshot,errors);
|
||||
internal void ResetForKVLoad() { _data=default(Data); }
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVEventPackState state)
|
||||
{
|
||||
if (state == null) throw new System.ArgumentNullException(nameof(state));
|
||||
_data=new Data { currentEventID=state.EventId,purchaseCount=state.PurchaseCount,
|
||||
VIPLvlWhenEventActivate=state.VipLevel,redirectID=state.RedirectId };
|
||||
}
|
||||
internal game.PlayerDataStorage.PlayerKVEventPackState SnapshotForKVValidation() =>
|
||||
new game.PlayerDataStorage.PlayerKVEventPackState(_data.currentEventID,_data.purchaseCount,_data.VIPLvlWhenEventActivate,_data.redirectID);
|
||||
public int CurrentEventID { get { return _data.currentEventID; } }
|
||||
public int PurchaseCount { get { return _data.purchaseCount; } }
|
||||
public int VIPLvlWhenEventActivated { get { return _data.VIPLvlWhenEventActivate; } }
|
||||
|
||||
public bool IsActive => _tables.TbFishingEvent.DataMap.ContainsKey(_data.currentEventID) &&
|
||||
(System.DateTime.Parse(
|
||||
(_tables.TbFishingEvent[_data.currentEventID].TimeDefinition as LimitedTime)
|
||||
?.EndTime) -
|
||||
ZZTimeHelper.UtcNow()).TotalSeconds > 0 &&
|
||||
PurchaseCount < _tables.TbEventPackManager[_data.redirectID].MaxCount;
|
||||
public EventPackData()
|
||||
{
|
||||
//Debug.Log("<color=red>construct</color>");
|
||||
_data = new Data();
|
||||
//PlayFabMgr.Instance.UpdateUserDataValue("EventPackData",
|
||||
// Newtonsoft.Json.JsonConvert.SerializeObject(_data));
|
||||
}
|
||||
|
||||
public void AddPurchaceCount()
|
||||
{
|
||||
_data.purchaseCount++;
|
||||
SaveEventPackData();
|
||||
}
|
||||
/// <summary>
|
||||
/// Refresh and update EventPackData if current event is not the event given. Will reset current
|
||||
/// event if the event given is null
|
||||
/// </summary>
|
||||
/// <param name="e">New event</param>
|
||||
public bool UpdateEventData(cfg.FishingEvent e = null)
|
||||
{
|
||||
bool isUpdated = false;
|
||||
if (e == null)
|
||||
{
|
||||
_data = new Data();
|
||||
isUpdated = true;
|
||||
}
|
||||
else if (_data.currentEventID != e.ID)
|
||||
{
|
||||
_data = new Data(e.ID, 0, GContext.container.Resolve<PlayerData>().PriceLv, e.RedirectID);
|
||||
isUpdated = true;
|
||||
}
|
||||
//else
|
||||
// do nothing
|
||||
SaveEventPackData();
|
||||
return isUpdated;
|
||||
}
|
||||
public void LoadEventPackData(string s)
|
||||
{
|
||||
//Debug.Log("<color=red>load epd</color>");
|
||||
_data = Newtonsoft.Json.JsonConvert.DeserializeObject<Data>(s);
|
||||
//Debug.Log($"load: {_data.currentEventID}");
|
||||
if (!_tables.TbFishingEvent.DataMap.ContainsKey(_data.currentEventID) ||
|
||||
(System.DateTime.Parse((_tables.TbFishingEvent[_data.currentEventID].TimeDefinition
|
||||
as LimitedTime).EndTime) - ZZTimeHelper.UtcNow()).TotalSeconds <= 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_tables.TbEventPackManager[_tables.TbFishingEvent[_data.currentEventID].RedirectID].PackType != 1)
|
||||
{
|
||||
_data = new Data();
|
||||
return;
|
||||
}
|
||||
if (_data.redirectID == 0)
|
||||
_data.redirectID = 4054001;
|
||||
TriggerEventPack();
|
||||
}
|
||||
public void SaveEventPackData()
|
||||
{
|
||||
playerSaveStore.Write("EventPackData",
|
||||
Newtonsoft.Json.JsonConvert.SerializeObject(_data)).RequireAccepted();
|
||||
}
|
||||
private void TriggerEventPack()
|
||||
{
|
||||
var tables = GContext.container.Resolve<Tables>();
|
||||
FishingEvent e = tables.TbFishingEvent.DataMap[_data.currentEventID];
|
||||
int packID = _data.redirectID;
|
||||
var packData = tables.TbEventPackManager.DataMap[packID];//pack data read from tables
|
||||
// if (_data.purchaseCount < packData.MaxCount && packData.PackType == 1)
|
||||
//TODO:change panel according to RedirecctID of current event
|
||||
// GContext.container.Resolve<IFaceUIService>().
|
||||
// AddGiftFaceUI(e.ID, UITypes.GiftPopupPanel_9, packData.PackType, true);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,993 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class EventPinballDataManager
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
|
||||
#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 奖励次数相关
|
||||
private int m_BonusResponseNumber = 10; // 相应次数
|
||||
public int BonusResponseNumber { set => m_BonusResponseNumber = value; }
|
||||
private int m_CumulativeResponseNumber = 0;
|
||||
public int CumulativeResponseNumber
|
||||
{
|
||||
get => m_CumulativeResponseNumber;
|
||||
set
|
||||
{
|
||||
m_CumulativeResponseNumber = value;
|
||||
FishingPinballAct.Publish(new EventPinballUpdateBonusCircularProgress());
|
||||
}
|
||||
}
|
||||
public bool IsAddScoreValid()
|
||||
{
|
||||
return m_CumulativeResponseNumber < m_BonusResponseNumber;
|
||||
}
|
||||
public float GetBonusResponseRatio()
|
||||
{
|
||||
if (m_BonusResponseNumber <= 0) return 0f;
|
||||
|
||||
return (float)m_CumulativeResponseNumber / m_BonusResponseNumber;
|
||||
}
|
||||
|
||||
private int m_AccumulatedTriggerCount = 0; // 埋点用
|
||||
public int AccumulatedTriggerCount
|
||||
{
|
||||
get => m_AccumulatedTriggerCount;
|
||||
set => m_AccumulatedTriggerCount = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 代币相关
|
||||
public int GetToken()
|
||||
{
|
||||
if (m_PinballData != null)
|
||||
return m_PinballData.Token;
|
||||
return 0;
|
||||
}
|
||||
public void SetRedPoint()
|
||||
{
|
||||
if (m_PinballData != null)
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Pinball, m_PinballData.Token > 0, m_PinballData.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Pinball, false);
|
||||
}
|
||||
}
|
||||
public void AddToken(int count)
|
||||
{
|
||||
if (m_PinballData != null)
|
||||
{
|
||||
m_PinballData.Token += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
GetFreshPinballData();
|
||||
if (m_PinballData == null) return;
|
||||
m_PinballData.Token += count;
|
||||
}
|
||||
|
||||
|
||||
SetRedPoint();
|
||||
FishingPinballAct.Publish<EventPinballTokenUpdateData>(new EventPinballTokenUpdateData());
|
||||
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(m_PinballData.EventID, m_PinballData.Token);
|
||||
|
||||
SyncData();
|
||||
}
|
||||
//public bool ConsumeTokensDefault()
|
||||
//{
|
||||
// EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
|
||||
// int magnification = GetCurMagnification();
|
||||
// bool isEnough = ConsumeTokens(pinballMain.SpinRequire * magnification);
|
||||
// if (isEnough == false)
|
||||
// {
|
||||
// ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_103"));
|
||||
// }
|
||||
// return isEnough;
|
||||
//}
|
||||
public bool ConsumeTokens(int count)
|
||||
{
|
||||
bool isEnough = IsEnough(count);
|
||||
if (isEnough == false) return false;
|
||||
|
||||
m_PinballData.ConsumedToken += count;
|
||||
AddToken(-count);
|
||||
return isEnough;
|
||||
}
|
||||
|
||||
public bool IsEnough(int spend)
|
||||
{
|
||||
if (m_PinballData == null) return false;
|
||||
return (m_PinballData.Token - spend) >= 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 每一局的临时数据
|
||||
public void ClearTemporaryData()
|
||||
{
|
||||
m_IsMultipleMax = false;
|
||||
|
||||
CumulativeResponseNumber = 0;
|
||||
AccumulatedTriggerCount = 0;
|
||||
|
||||
ClearTemporaryScore();
|
||||
ClearTemporaryConsumedToken();
|
||||
}
|
||||
|
||||
private int m_TemporaryScore = 0;
|
||||
public int TemporaryScore { get => m_TemporaryScore; }
|
||||
|
||||
public void ClearTemporaryScore()
|
||||
{
|
||||
m_TemporaryScore = 0;
|
||||
}
|
||||
public void AddTemporaryScore(int score)
|
||||
{
|
||||
m_TemporaryScore += score;
|
||||
}
|
||||
public void MultiplyTemporaryScore(int multiple)
|
||||
{
|
||||
m_TemporaryScore *= multiple;
|
||||
}
|
||||
|
||||
public void ReturnConsumedToken()
|
||||
{
|
||||
AddToken(m_TemporaryConsumedToken);
|
||||
}
|
||||
private int m_TemporaryConsumedToken = 0;
|
||||
public int TemporaryConsumedToken { get => m_TemporaryConsumedToken; }
|
||||
public void ClearTemporaryConsumedToken()
|
||||
{
|
||||
m_TemporaryConsumedToken = 0;
|
||||
}
|
||||
public bool ConsumeTemporaryTokensDefault()
|
||||
{
|
||||
EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
|
||||
int magnification = GetCurMagnification();
|
||||
bool isEnough = ConsumeTemporaryTokens(pinballMain.SpinRequire * magnification);
|
||||
//if (isEnough == false)
|
||||
//{
|
||||
// ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_103"));
|
||||
//}
|
||||
return isEnough;
|
||||
}
|
||||
public bool ConsumeTemporaryTokens(int count)
|
||||
{
|
||||
bool isEnough = IsEnough(count);
|
||||
if (isEnough == false) return false;
|
||||
|
||||
ConsumeTokens(count);
|
||||
|
||||
m_TemporaryConsumedToken += count;
|
||||
FishingPinballAct.Publish<EventPinballTokenUpdateData>(new EventPinballTokenUpdateData());
|
||||
return isEnough;
|
||||
}
|
||||
|
||||
private bool m_IsMultipleMax = false;
|
||||
public bool IsMultipleMax { get => m_IsMultipleMax; set => m_IsMultipleMax = value; }
|
||||
#endregion
|
||||
|
||||
#region 分数相关
|
||||
private int m_MagnificationListIndex = 0;
|
||||
public void ResetMagnification()
|
||||
{
|
||||
EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
List<int> MagnificationList = pinballMain.SpinMag;
|
||||
int magnification = GetCurMagnification();
|
||||
bool isEnough = IsEnough(pinballMain.SpinRequire * magnification);
|
||||
|
||||
if (isEnough == false)
|
||||
{
|
||||
int count = MagnificationList.Count;
|
||||
int index = 0;
|
||||
for (int i = count - 1; i >= 0; --i)
|
||||
{
|
||||
int tmpMagnification = MagnificationList[i];
|
||||
if (IsEnough(pinballMain.SpinRequire * tmpMagnification))
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_MagnificationListIndex = index;
|
||||
}
|
||||
|
||||
FishingPinballAct.Publish(new EventPinballResetMagnification());
|
||||
}
|
||||
public int GetCurMagnification()
|
||||
{
|
||||
if (m_PinballData == null) return -1;
|
||||
EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
List<int> MagnificationList = pinballMain.SpinMag;
|
||||
return MagnificationList[m_MagnificationListIndex];
|
||||
}
|
||||
public bool IsMaxMagnification()
|
||||
{
|
||||
if (m_PinballData == null) return false;
|
||||
|
||||
return IsCurMaxMagnification();// m_MagnificationListIndex == (m_PinballData.MagnificationList.Count - 1);
|
||||
}
|
||||
bool IsCurMaxMagnification()
|
||||
{
|
||||
if (m_PinballData == null) return false;
|
||||
EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
List<int> MagnificationList = pinballMain.SpinMag;
|
||||
int count = MagnificationList.Count;
|
||||
|
||||
int tmpIndex = m_MagnificationListIndex + 1;
|
||||
|
||||
if (tmpIndex >= count)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int magnification = MagnificationList[tmpIndex];
|
||||
return !IsEnough(pinballMain.SpinRequire * magnification);
|
||||
}
|
||||
public void ScrollToTheNextMagnification()
|
||||
{
|
||||
if (m_PinballData == null) return;
|
||||
|
||||
EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
List<int> MagnificationList = pinballMain.SpinMag;
|
||||
|
||||
int count = MagnificationList.Count;
|
||||
|
||||
int tmpIndex = m_MagnificationListIndex + 1;
|
||||
|
||||
if (tmpIndex >= count)
|
||||
{
|
||||
tmpIndex = 0;
|
||||
}
|
||||
|
||||
int magnification = MagnificationList[tmpIndex];
|
||||
bool isEnough = IsEnough(pinballMain.SpinRequire * magnification);
|
||||
|
||||
if (isEnough)
|
||||
{
|
||||
m_MagnificationListIndex = tmpIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_MagnificationListIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private List<float> m_RewardList = new List<float>();
|
||||
public List<float> RewardList { get => m_RewardList; }
|
||||
public void InitRewardList()
|
||||
{
|
||||
m_RewardList.Clear();
|
||||
|
||||
int count = m_TbEventPinballReward.DataList.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var item = m_TbEventPinballReward.DataList[i];
|
||||
m_RewardList.Add(item.SpinPoint);
|
||||
}
|
||||
if (m_PinballData != null)
|
||||
{
|
||||
if (m_PinballData.Scores >= m_RewardList[^1])
|
||||
{
|
||||
m_PinballData.Scores = 0;
|
||||
}
|
||||
m_PinballData.RewardIndex = FindInsertionIndex(m_RewardList, m_PinballData.Scores);
|
||||
}
|
||||
}
|
||||
//private int m_RewardIndex, m_RewardMaxIndex;
|
||||
//public int RewardIndex { get => m_RewardIndex; }
|
||||
//public int RewardMaxIndex { get => m_RewardMaxIndex; }
|
||||
//public void CalculateReward()
|
||||
//{
|
||||
// if (m_PinballData == null) return;
|
||||
|
||||
// m_RewardIndex = FindInsertionIndex(m_RewardList, m_PinballData.Scores);
|
||||
// m_RewardMaxIndex = m_TbEventPinballReward.DataList.Count - 1;
|
||||
//}
|
||||
//public void AddScore(int score)
|
||||
//{
|
||||
// if (m_PinballData == null) return;
|
||||
|
||||
// m_PinballData.Scores += score;
|
||||
// m_PinballData.TotalScores += score;
|
||||
//}
|
||||
#endregion
|
||||
|
||||
#region 活动相关
|
||||
public void ActivateEvent(FishingEvent fishingEvent)
|
||||
{
|
||||
if (fishingEvent == null) return;
|
||||
|
||||
if (!Init()) return;
|
||||
|
||||
if (m_PinballData != null) FixPinballData();
|
||||
if (m_PinballData == null) GetFreshPinballData();
|
||||
}
|
||||
|
||||
public void GetDataFromServer(string json)
|
||||
{
|
||||
if (string.IsNullOrEmpty(json))
|
||||
{
|
||||
GameDebug.LogWarning("EventPinball get data from server has encountered empty data.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_PinballData = Newtonsoft.Json.JsonConvert.DeserializeObject<EventPinballData>(json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GameDebug.LogWarning("EventPinball deserialize data has failed. Exception: " + e.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Init()) return;
|
||||
}
|
||||
public int FixPinballData()
|
||||
{
|
||||
if (m_PinballData == null) return 0;
|
||||
|
||||
// 获取当前开放并符合解锁条件的弹珠活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(4, 6, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return ClearData();
|
||||
int eventID = fishingEvent.ID;
|
||||
int pinballMainID = fishingEvent.RedirectID;
|
||||
if (pinballMainID < 0) return ClearData();
|
||||
|
||||
if (eventID != m_PinballData.EventID || pinballMainID != m_PinballData.PinballMainID) return ClearData();
|
||||
|
||||
return 0;
|
||||
}
|
||||
public TimeSpan RemainingTime
|
||||
{
|
||||
get => DateTime.Parse((m_Tables.TbFishingEvent[m_PinballData.EventID].TimeDefinition as LimitedTime).EndTime)
|
||||
- ZZTimeHelper.UtcNow();
|
||||
}
|
||||
public int ClearData()
|
||||
{
|
||||
if (m_PinballData == null) return 0;
|
||||
m_PinballData = null;
|
||||
return 0;
|
||||
}
|
||||
public void SyncData()
|
||||
{
|
||||
if (!m_IsSyncData) return;
|
||||
playerSaveStore.Write(EventPinballConfig.SAVE_DATA_KEY, Newtonsoft.Json.JsonConvert.SerializeObject(m_PinballData)).RequireAccepted();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
public bool Init()
|
||||
{
|
||||
InitTables();
|
||||
|
||||
m_IsSimulatorMode = false;
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Table Data
|
||||
private Tables m_Tables;
|
||||
private TbEventPinballMain m_TbEventPinballMain;
|
||||
private TbEventPinballPoint m_TbEventPinballPoint;
|
||||
private TbEventPinballReward m_TbEventPinballReward;
|
||||
private void InitTables()
|
||||
{
|
||||
m_Tables = GContext.container.Resolve<Tables>();
|
||||
if (m_Tables == null) return;
|
||||
m_TbEventPinballMain = m_Tables.TbEventPinballMain;
|
||||
m_TbEventPinballPoint = m_Tables.TbEventPinballPoint;
|
||||
m_TbEventPinballReward = m_Tables.TbEventPinballReward;
|
||||
|
||||
InitRewardList();
|
||||
}
|
||||
public EventPinballReward GetEventPinballReward(int index)
|
||||
{
|
||||
return m_TbEventPinballReward.DataList[index];
|
||||
}
|
||||
|
||||
public int GetTicketItemId()
|
||||
{
|
||||
return m_Tables.TbFishingEventCycleItem[m_Tables.TbFishingEvent[m_PinballData.EventID].RedirectID].ItemId;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region UI 设置相关
|
||||
private UIType m_UIPanel = null, m_InfoPanel = null;
|
||||
public void ClearUIData()
|
||||
{
|
||||
m_UIPanel = null;
|
||||
m_InfoPanel = null;
|
||||
}
|
||||
public void InitUIData()
|
||||
{
|
||||
GetUIPanel();
|
||||
GetInfoPanel();
|
||||
}
|
||||
public UIType GetUIPanel()
|
||||
{
|
||||
if (m_UIPanel == null)
|
||||
{
|
||||
var data = GetCurrentEventPinballMain();
|
||||
m_UIPanel = new UIType(data.UIPanel);
|
||||
}
|
||||
return m_UIPanel;
|
||||
}
|
||||
public UIType GetInfoPanel()
|
||||
{
|
||||
if (m_InfoPanel == null)
|
||||
{
|
||||
var data = GetCurrentEventPinballMain();
|
||||
m_InfoPanel = new UIType(data.InfoPanel);//EventPinballRewardPoupPanel
|
||||
}
|
||||
return m_InfoPanel;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 数据相关
|
||||
private EventPinballData m_PinballData;
|
||||
internal bool IsInactiveForKVValidation => m_Tables==null && m_TbEventPinballMain==null && m_TbEventPinballPoint==null &&
|
||||
m_TbEventPinballReward==null && m_UIPanel==null && m_InfoPanel==null && m_RewardList.Count==0 &&
|
||||
m_PointWeightList.Count==0 && !m_IsSimulatorMode && !m_IsMultipleMax && m_CumulativeResponseNumber==0 &&
|
||||
m_AccumulatedTriggerCount==0 && m_TemporaryScore==0 && m_TemporaryConsumedToken==0 && m_MagnificationListIndex==0 && m_BonusResponseNumber==10;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVPinballState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodePinball(raw,snapshot,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
m_PinballData=null; m_BonusResponseNumber=10; m_CumulativeResponseNumber=m_AccumulatedTriggerCount=m_TemporaryScore=m_TemporaryConsumedToken=m_MagnificationListIndex=0;
|
||||
m_IsMultipleMax=false; m_IsSimulatorMode=false; m_RewardList=new List<float>(); m_PointWeightList=new List<float>(); m_UIPanel=m_InfoPanel=null;
|
||||
m_Tables=null; m_TbEventPinballMain=null; m_TbEventPinballPoint=null; m_TbEventPinballReward=null;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVPinballState state)
|
||||
{
|
||||
if(state==null) throw new ArgumentNullException(nameof(state));
|
||||
m_PinballData=!state.IsPresent ? null : new EventPinballData
|
||||
{
|
||||
EventID=state.EventID,
|
||||
PinballMainID=state.PinballMainID,
|
||||
SpinPoint=state.SpinPoint,
|
||||
Scores=state.Scores,
|
||||
Multiple=state.Multiple,
|
||||
Token=state.Token,
|
||||
ConsumedToken=state.ConsumedToken,
|
||||
RewardIndex=state.RewardIndex,
|
||||
Force=state.Force,
|
||||
LuckyValue=state.LuckyValue,
|
||||
InflationRate=state.InflationRate
|
||||
};
|
||||
}
|
||||
public EventPinballData PinballData { get { return m_PinballData; } }
|
||||
public int RewardMaxIndex
|
||||
{
|
||||
get { return m_TbEventPinballReward.DataList.Count - 1; }
|
||||
}
|
||||
public EventPinballMain GetFreshPinballData()
|
||||
{
|
||||
// 获取当前开放并符合解锁条件的弹珠活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(4, 6, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return null;
|
||||
int eventID = fishingEvent.ID;
|
||||
int pinballMainID = fishingEvent.RedirectID;
|
||||
if (pinballMainID < 0) return null;
|
||||
|
||||
// 获取当前弹珠活动数据
|
||||
EventPinballMain pinballMain = m_TbEventPinballMain.GetOrDefault(pinballMainID);
|
||||
if (pinballMain == null) return null;
|
||||
|
||||
int token = m_DefaultToken;
|
||||
m_PinballData = null;
|
||||
|
||||
token = GContext.container.Resolve<FishingEventData>().GetInitWelcomeGift(eventID);
|
||||
|
||||
m_PinballData = new EventPinballData();
|
||||
|
||||
m_PinballData.InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
Debug.Log("m_PinballData.InflationRate " + GContext.container.Resolve<PlayerData>().InflationRate);
|
||||
m_PinballData.EventID = eventID;
|
||||
m_PinballData.PinballMainID = pinballMainID;
|
||||
m_PinballData.Force = 0f;
|
||||
m_PinballData.SpinPoint = 0;
|
||||
m_PinballData.Scores = 0;
|
||||
m_PinballData.Multiple = 0;
|
||||
|
||||
m_PinballData.Token = token;
|
||||
m_PinballData.ConsumedToken = 0;
|
||||
|
||||
m_PinballData.LuckyValue = (float)pinballMain.InitialLuck;
|
||||
|
||||
m_PinballData.RewardIndex = 0;
|
||||
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(m_PinballData.EventID, m_PinballData.Token);
|
||||
|
||||
|
||||
UpdateLuckyValueAndWeight();
|
||||
|
||||
GContext.container.Resolve<GuideDataCenter>().ClearGuide("");
|
||||
SetRedPoint();
|
||||
|
||||
// 同步数据
|
||||
SyncData();
|
||||
return pinballMain;
|
||||
}
|
||||
public EventPinballMain GetNextPinballData()
|
||||
{
|
||||
// 获取当前开放并符合解锁条件的弹珠活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(4, 6, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return null;
|
||||
int eventID = fishingEvent.ID;
|
||||
int pinballMainID = fishingEvent.RedirectID;
|
||||
if (pinballMainID < 0) return null;
|
||||
|
||||
if (eventID != m_PinballData.EventID || pinballMainID != m_PinballData.PinballMainID) return GetFreshPinballData();
|
||||
|
||||
// 获取当前弹珠活动数据
|
||||
EventPinballMain pinballMain = m_TbEventPinballMain.GetOrDefault(pinballMainID);
|
||||
if (pinballMain == null) return null;
|
||||
|
||||
m_PinballData.EventID = eventID;
|
||||
m_PinballData.PinballMainID = pinballMainID;
|
||||
|
||||
m_PinballData.ConsumedToken = 0;
|
||||
|
||||
UpdateLuckyValueAndWeight();
|
||||
|
||||
// 同步数据
|
||||
SyncData();
|
||||
return pinballMain;
|
||||
}
|
||||
public EventPinballMain ContinueGame()
|
||||
{
|
||||
if (m_PinballData == null) return GetFreshPinballData();
|
||||
|
||||
// 获取当前开放并符合解锁条件的弹珠活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
FishingEvent fishingEvent = fishingEventData.GetEventByTypeAndSubType(4, 6, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return null;
|
||||
int eventID = fishingEvent.ID;
|
||||
int pinballMainID = fishingEvent.RedirectID;
|
||||
if (pinballMainID < 0) return null;
|
||||
|
||||
if (eventID != m_PinballData.EventID || pinballMainID != m_PinballData.PinballMainID) return GetFreshPinballData();
|
||||
|
||||
// 获取当前弹珠活动数据
|
||||
EventPinballMain pinballMain = m_TbEventPinballMain.GetOrDefault(pinballMainID);
|
||||
if (pinballMain == null) return null;
|
||||
|
||||
//UpdateLuckyValueAndWeight();
|
||||
|
||||
return pinballMain;
|
||||
}
|
||||
public List<float> m_PointWeightList = new List<float>();
|
||||
public EventPinballPoint UpdateLuckyValueAndWeight()
|
||||
{
|
||||
if (m_TbEventPinballPoint == null) return null;
|
||||
|
||||
m_PointWeightList.Clear();
|
||||
|
||||
float luckyValue = m_PinballData.LuckyValue;
|
||||
int count = m_TbEventPinballPoint.DataList.Count;
|
||||
|
||||
float total = 0;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
EventPinballPoint item = m_TbEventPinballPoint.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);
|
||||
|
||||
EventPinballPoint target = m_TbEventPinballPoint.DataList[index];
|
||||
if (float.MaxValue - target.DeltaLuck <= m_PinballData.LuckyValue)
|
||||
{
|
||||
m_PinballData.LuckyValue = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PinballData.LuckyValue += target.DeltaLuck;
|
||||
if (m_PinballData.LuckyValue < 0f)
|
||||
{
|
||||
m_PinballData.LuckyValue = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
m_PinballData.SpinPoint = target.SpinPoint;
|
||||
|
||||
return target;
|
||||
}
|
||||
public int FindInsertionIndex(List<float> sortedList, float target)
|
||||
{
|
||||
if (sortedList == null || sortedList.Count == 0)
|
||||
{
|
||||
Debug.LogError("---- EventPinballDataManager->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 float GetRandomForce(float ratio)
|
||||
{
|
||||
EventPinballPoint target = m_TbEventPinballPoint.GetOrDefault(m_PinballData.SpinPoint);
|
||||
EventPinballMain pinballMain = GetCurrentEventPinballMain();
|
||||
float failMaxDist = pinballMain.FailMaxDist;
|
||||
if (ratio <= failMaxDist)
|
||||
{
|
||||
return pinballMain.FailMaxForce;
|
||||
}
|
||||
int count = target.ForceLv.Count;
|
||||
|
||||
List<float> forceList = target.ForceLv[count - 1];
|
||||
ratio = (ratio - failMaxDist) / 1 - failMaxDist; // 归一化到0-1之间
|
||||
float subRatio = 1f / count; // 每个等级的比例
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
if (ratio <= subRatio * (i + 1))
|
||||
{
|
||||
forceList = target.ForceLv[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
count = forceList.Count;
|
||||
int index = UnityEngine.Random.Range(0, count);
|
||||
return forceList[index];
|
||||
}
|
||||
|
||||
public void InitGameRound()
|
||||
{
|
||||
//SetRewardList();
|
||||
//CalculateReward();
|
||||
ResetMagnification();
|
||||
ClearTemporaryData();
|
||||
|
||||
m_PinballData.Force = 0f;
|
||||
PinballData.Multiple = 0;
|
||||
}
|
||||
|
||||
public EventPinballMain GetCurrentEventPinballMain()
|
||||
{
|
||||
if (m_PinballData == null) return GetFreshPinballData();
|
||||
|
||||
return m_TbEventPinballMain.GetOrDefault(m_PinballData.PinballMainID);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 本地存储数据相关
|
||||
public string GetSaveKey(string key)
|
||||
{
|
||||
if (m_PinballData == null) return key;
|
||||
return m_PinballData.EventID + "-" + m_PinballData.PinballMainID + "-" + 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();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 红点相关
|
||||
public bool IsRedDotDisplayed()
|
||||
{
|
||||
if (m_PinballData == null) return false;
|
||||
return m_PinballData.Token > 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 运行模式相关(模拟数据模式/正常玩家模式)
|
||||
public bool m_IsSimulatorMode = false;
|
||||
public bool IsSimulatorMode
|
||||
{
|
||||
get => m_IsSimulatorMode;
|
||||
set
|
||||
{
|
||||
if (m_IsSimulatorMode != value)
|
||||
{
|
||||
m_IsSimulatorMode = value;
|
||||
FishingPinballAct.Publish(new EventPinballSyncOperatingModeData()
|
||||
{
|
||||
IsSimulatorMode = value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IO
|
||||
public string GetSaveFilepath(string filename)
|
||||
{
|
||||
return Path.Combine(Application.persistentDataPath, filename);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class EventPinballData
|
||||
{
|
||||
public int EventID { get; set; }
|
||||
public int PinballMainID { get; set; }
|
||||
public float Force { get; set; }
|
||||
public int SpinPoint { get; set; }
|
||||
public int Scores { get; set; }
|
||||
public int Multiple { get; set; }
|
||||
|
||||
public int Token { get; set; }
|
||||
public int ConsumedToken { get; set; }
|
||||
|
||||
public float LuckyValue { get; set; }
|
||||
|
||||
public int RewardIndex { get; set; }
|
||||
|
||||
public float InflationRate { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public enum EventPinballBonusType
|
||||
{
|
||||
TypeScore,
|
||||
TypeMultiple
|
||||
}
|
||||
|
||||
public enum EventPinballTrackType
|
||||
{
|
||||
TypeEnter,
|
||||
TypeExit
|
||||
}
|
||||
|
||||
public enum EventPinballState
|
||||
{
|
||||
Prepare,
|
||||
Running,
|
||||
Ended
|
||||
}
|
||||
|
||||
public enum EventPinballSwitcherState
|
||||
{
|
||||
Open,
|
||||
Close
|
||||
}
|
||||
|
||||
public class EventPinballSwitcherOpen
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballSwitcherClose
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballEnergyArrowData
|
||||
{
|
||||
public float Energy;
|
||||
}
|
||||
|
||||
public class EventPinballEnergyArrowRestoreData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballUpdateBonusCircularProgress
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballTokenUpdateData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballBasicScore
|
||||
{
|
||||
public int Score;
|
||||
}
|
||||
public class EventPinballBasicScoreHideData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventPinballBasicScoreShowData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballResetMagnification
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballRewardUpdateData
|
||||
{
|
||||
public int Scores;
|
||||
}
|
||||
|
||||
public class EventPinballSpringUpdateData
|
||||
{
|
||||
public float Ratio;
|
||||
}
|
||||
|
||||
public class EventPinballPlayArrowBreathAnimData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventPinballStopArrowBreathAnimData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventPinballPlayLightOnAnimData
|
||||
{
|
||||
|
||||
}
|
||||
public class EventPinballResumePelletData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballResetPelletStatusData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballLaunchPelletData
|
||||
{
|
||||
public float Force;
|
||||
}
|
||||
|
||||
public class EventPinballShowPopupData
|
||||
{
|
||||
public int Scores;
|
||||
public EventPinballBonusType BonusType;
|
||||
public bool IsMultipleMax;
|
||||
}
|
||||
public class EventPinballHidePopupData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballShowMultipleTextData
|
||||
{
|
||||
public EventPinballBonusType BonusType;
|
||||
public Vector3 Position;
|
||||
public string Tips;
|
||||
public int Scores;
|
||||
public bool IsMultipleMax;
|
||||
}
|
||||
|
||||
public class EventPinballRewardDisplayUpdateData
|
||||
{
|
||||
public int RewardIndex;
|
||||
public int TargetScore;
|
||||
public int LevelScore;
|
||||
public float FromRatio;
|
||||
public float TargetRatio;
|
||||
public int DropId;
|
||||
public int RewardDisplayDropId;
|
||||
}
|
||||
|
||||
public class EventPinballSyncOperatingModeData
|
||||
{
|
||||
public bool IsSimulatorMode;
|
||||
}
|
||||
|
||||
public class EventPinballHideWidgetMultipleEffectData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballShowWidgetScoreEffect2Data
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballHideFiringPinData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballShowFiringPinData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballHideCircularRotationalAnim
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballShowCircularRotationalAnim
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballLaunchPelletStatusData
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class EventPinballSetDummyPelletData
|
||||
{
|
||||
public PinballPellet Pellet;
|
||||
}
|
||||
|
||||
public class EventPinballConfig
|
||||
{
|
||||
public const string CSVFileName = "pinball.csv";
|
||||
|
||||
public const string SAVE_DATA_KEY = "epbdk"; // event pinball data key
|
||||
|
||||
public const int MultipleMax = 10;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,957 @@
|
||||
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
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
#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", ScratchTicketData.magIndex + 1)
|
||||
.AddContent("milestone_task", data.MilestoneTask);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log("<color=red>--- RecordStatisticalData m_RecordList: </color>" + Newtonsoft.Json.JsonConvert.SerializeObject(m_RecordList));
|
||||
#endif
|
||||
}
|
||||
private List<EventScratchTicketRecordData> m_RecordList = new List<EventScratchTicketRecordData>();
|
||||
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;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 数据相关
|
||||
private EventScratchTicketData m_ScratchTicketData;
|
||||
internal bool IsInactiveForKVValidation => m_Tables==null && m_TbEventScratchMain==null && m_TbEventScratchCard==null &&
|
||||
m_TbEventScratchReward==null && m_ChainPackData==null && m_NormalPackData==null && m_SpriteAtlas==null && m_SpriteDict==null &&
|
||||
m_UIPanel==null && m_InfoPanel==null && m_RewardPanel==null && m_ChainPackPanel==null && m_PackPanel==null &&
|
||||
m_RecordList.Count==0 && m_PointWeightList.Count==0 && m_ScratchStampList.Count==0 &&
|
||||
TempRecordRewardTaskID==0 && TempRecordRewardTaskConsumedToken==0;
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVScratchState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeScratch(raw,snapshot,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
m_ScratchTicketData=null; TempRecordRewardTaskID=TempRecordRewardTaskConsumedToken=0; m_RecordList=new List<EventScratchTicketRecordData>();
|
||||
m_PointWeightList=new List<float>(); m_ScratchStampList=new List<int>(); m_ChainPackData=null; m_NormalPackData=null;
|
||||
m_UIPanel=m_InfoPanel=m_RewardPanel=m_ChainPackPanel=m_PackPanel=null; m_SpriteAtlas=null; m_SpriteDict=null;
|
||||
m_Tables=null; m_TbEventScratchMain=null; m_TbEventScratchCard=null; m_TbEventScratchReward=null;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVScratchState state)
|
||||
{
|
||||
if(state==null) throw new ArgumentNullException(nameof(state));
|
||||
m_ScratchTicketData=!state.IsPresent ? null : new EventScratchTicketData
|
||||
{
|
||||
EventID=state.EventID,
|
||||
ScratchMainID=state.ScratchMainID,
|
||||
ChainPackID=state.ChainPackID,
|
||||
Token=state.Token,
|
||||
ConsumedToken=state.ConsumedToken,
|
||||
RewardTaskConsumedToken=state.RewardTaskConsumedToken,
|
||||
EventScratchRewardTaskID=state.EventScratchRewardTaskID,
|
||||
EventScratchCardID=state.EventScratchCardID,
|
||||
TotalConsumedToken=state.TotalConsumedToken,
|
||||
ChainPackProgress=state.ChainPackProgress,
|
||||
magIndex=state.magIndex,
|
||||
IsOver=state.IsOver,
|
||||
LuckyValue=state.LuckyValue,
|
||||
InflationRate=state.InflationRate,
|
||||
StampRewardList=new List<int>(state.StampRewardList),
|
||||
StampRewardDict=state.StampRewardDict.ToDictionary(p=>p.Key,p=>new EventScratchTicketOwnedStampRewardData { EventScratchCardID=p.Value.EventScratchCardID,Count=p.Value.Count })
|
||||
};
|
||||
}
|
||||
public EventScratchTicketData ScratchTicketData { get { return m_ScratchTicketData; } }
|
||||
public EventScratchMain GetFreshScratchTicketData()
|
||||
{
|
||||
// 获取当前开放并符合解锁条件的刮刮乐活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
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<FishingEventData>().GetInitWelcomeGift(eventID);
|
||||
|
||||
m_ScratchTicketData = new EventScratchTicketData();
|
||||
m_ScratchTicketData.InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
Debug.Log("m_PinballData.InflationRate " + GContext.container.Resolve<PlayerData>().InflationRate);
|
||||
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<int, EventScratchTicketOwnedStampRewardData>();
|
||||
|
||||
EventScratchReward eventScratchReward = m_TbEventScratchReward.DataList[0];
|
||||
m_ScratchTicketData.EventScratchRewardTaskID = eventScratchReward.TaskID;
|
||||
m_ScratchTicketData.RewardTaskConsumedToken = 0;
|
||||
if (FishingEventData.Condition_NoRechargeDay())
|
||||
{
|
||||
m_ScratchTicketData.ChainPackID = scratchMain.AdPackId;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ScratchTicketData.ChainPackID = scratchMain.PackId;
|
||||
}
|
||||
m_ScratchTicketData.StampRewardList.Clear();
|
||||
|
||||
ClearSaveData(EventScratchTicketConfig.key_first_in);
|
||||
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(m_ScratchTicketData.EventID, m_ScratchTicketData.Token);
|
||||
|
||||
UpdateLuckyValueAndWeight();
|
||||
|
||||
foreach (var item in scratchMain.GuidanceGroupList)
|
||||
{
|
||||
GContext.container.Resolve<GuideDataCenter>().ClearGuide(item);
|
||||
}
|
||||
|
||||
// 同步数据
|
||||
SyncData();
|
||||
return scratchMain;
|
||||
}
|
||||
|
||||
public EventScratchMain GetNextScratchTicketData()
|
||||
{
|
||||
// 获取当前开放并符合解锁条件的刮刮乐活动ID
|
||||
FishingEventData fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
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<FishingEventData>();
|
||||
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<float> m_PointWeightList = new List<float>();
|
||||
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<float> 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;
|
||||
int packId = m_ScratchTicketData.ChainPackID;
|
||||
if (packId == 0)
|
||||
{
|
||||
packId = scratchMain.PackId;
|
||||
}
|
||||
var chainList = m_Tables.TbEventPackManager[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, m_ChainPackData.RedPointIndex);
|
||||
}
|
||||
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<IDeferredRewardStashService>().Reset();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
private List<int> m_ScratchStampList = new List<int>();
|
||||
public List<int> 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 int GetRedCount()
|
||||
{
|
||||
if (m_ScratchTicketData != null)
|
||||
{
|
||||
//+礼包红点
|
||||
if (m_ChainPackData != null)
|
||||
{
|
||||
return m_ScratchTicketData.Token + m_ChainPackData.RedPointIndex;
|
||||
|
||||
}
|
||||
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, GetRedCount());
|
||||
else RedPointManager.Instance.SetRedPointState(RedPointName.Home_ScratchTicket, false);
|
||||
|
||||
FishingScratchTicketAct.Publish<EventScratchTicketTokenUpdateData>(new EventScratchTicketTokenUpdateData());
|
||||
|
||||
GContext.container.Resolve<FishingEventData>().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<EventScratchTicketData>(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<FishingEventData>();
|
||||
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;
|
||||
playerSaveStore.Write(EventScratchTicketConfig.SAVE_DATA_KEY, Newtonsoft.Json.JsonConvert.SerializeObject(m_ScratchTicketData)).RequireAccepted();
|
||||
}
|
||||
|
||||
List<int> magList = new List<int>() { 1, 5, 10, 20 };
|
||||
|
||||
public int ChangeScratchMag()
|
||||
{
|
||||
ScratchTicketData.magIndex++;
|
||||
if (ScratchTicketData.magIndex >= magList.Count)
|
||||
{
|
||||
ScratchTicketData.magIndex = 0;
|
||||
}
|
||||
SyncData();
|
||||
return magList[ScratchTicketData.magIndex];
|
||||
}
|
||||
public int GetScratchMag()
|
||||
{
|
||||
return magList[ScratchTicketData.magIndex];
|
||||
}
|
||||
public bool IsMaxMag()
|
||||
{
|
||||
return ScratchTicketData.magIndex == magList.Count - 1;
|
||||
}
|
||||
#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();
|
||||
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<string, Sprite> m_SpriteDict;
|
||||
private void InitSpriteDict()
|
||||
{
|
||||
if (m_SpriteDict != null)
|
||||
{
|
||||
m_SpriteDict.Clear();
|
||||
m_SpriteDict = null;
|
||||
}
|
||||
m_SpriteDict = new Dictionary<string, Sprite>();
|
||||
}
|
||||
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 void InitTables()
|
||||
{
|
||||
m_Tables = GContext.container.Resolve<Tables>();
|
||||
if (m_Tables == null) return;
|
||||
|
||||
m_TbEventScratchMain = m_Tables.TbEventScratchMain;
|
||||
m_TbEventScratchCard = m_Tables.TbEventScratchCard;
|
||||
m_TbEventScratchReward = m_Tables.TbEventScratchReward;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 红点相关
|
||||
public bool IsRedDotDisplayed()
|
||||
{
|
||||
if (m_ScratchTicketData == null) return false;
|
||||
return m_ScratchTicketData.Token > 0;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
public enum EventScratchTicketScratchType
|
||||
{
|
||||
TypeInvalid,
|
||||
TypeScratch1,
|
||||
TypeScratch5,//5不是代表5次 表示多数
|
||||
TypeTemporary
|
||||
}
|
||||
public class EventScratchTicketData
|
||||
{
|
||||
public int EventID { get; set; }
|
||||
public int ScratchMainID { get; set; }
|
||||
public int ChainPackID { 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<int> StampRewardList = new List<int>();
|
||||
public float InflationRate { get; set; }
|
||||
|
||||
public Dictionary<int, EventScratchTicketOwnedStampRewardData> StampRewardDict { get; set; }
|
||||
public int magIndex { get; set; }
|
||||
public void Clear()
|
||||
{
|
||||
if (StampRewardDict != null)
|
||||
{
|
||||
StampRewardDict.Clear();
|
||||
}
|
||||
StampRewardList.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 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 MilestoneTask;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,84 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using LitJson;
|
||||
using System;
|
||||
namespace GameCore
|
||||
{
|
||||
public partial class FishingEventData
|
||||
{
|
||||
string Pack1A1DataKey = "Pack1A1Data";
|
||||
public TriggerPackBuyData Pack1A1Data;
|
||||
void GiftFace1A1(int packType, int eventID)
|
||||
{
|
||||
if (Pack1A1Data == null || Pack1A1Data.index < 2 || Pack1A1Data.lastID != eventID)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(eventID, UITypes.GiftPopupPanel_10, packType);
|
||||
}
|
||||
}
|
||||
public void SetPack1A1(FishingEvent t)
|
||||
{
|
||||
if (Pack1A1Data == null)
|
||||
{
|
||||
Pack1A1Data = ReadEventArchive<TriggerPackBuyData>(Pack1A1DataKey);
|
||||
}
|
||||
EventPackManager eventPackDataRead = _tables.TbEventPackManager.DataMap[t.RedirectID];
|
||||
GiftFace1A1(eventPackDataRead.PackType, t.ID);
|
||||
if (Pack1A1Data != null && Pack1A1Data.lastID == t.ID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Pack1A1Data = new TriggerPackBuyData()
|
||||
{
|
||||
ID = eventPackDataRead.ID,
|
||||
lastID = t.ID,//活动id
|
||||
index = 0,
|
||||
vipLevel = GContext.container.Resolve<PlayerData>().PriceLv,
|
||||
mapID = GContext.container.Resolve<PlayerData>().lastMapId,
|
||||
};
|
||||
RedPointManager.Instance.SetRedPointState(HomeBtnOneOnePack.redKey, false);
|
||||
|
||||
playerSaveStore.Write(Pack1A1DataKey, JsonMapper.ToJson(Pack1A1Data)).RequireAccepted();
|
||||
}
|
||||
public void SetPack1A1Index(int index)
|
||||
{
|
||||
if (Pack1A1Data == null || index < Pack1A1Data.index)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Pack1A1Data.index = index;
|
||||
if (Pack1A1Data.index >= 2)
|
||||
{
|
||||
//购买完 特殊处理
|
||||
GContext.Publish(new TargetEvent(Pack1A1Data.lastID, 3, 11));
|
||||
}
|
||||
RedPointManager.Instance.SetRedPointState(HomeBtnOneOnePack.redKey, Pack1A1Data.index == 1);
|
||||
playerSaveStore.Write(Pack1A1DataKey, JsonMapper.ToJson(Pack1A1Data)).RequireAccepted();
|
||||
}
|
||||
public int GetPack1A1Index()
|
||||
{
|
||||
if (Pack1A1Data == null)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
return Pack1A1Data.index;
|
||||
}
|
||||
public EventPackManager Get1A1EventPackManager()
|
||||
{
|
||||
if (Pack1A1Data == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return _tables.TbEventPackManager.DataMap[Pack1A1Data.ID];
|
||||
}
|
||||
public DateTime GetEvent1A1EndTime()
|
||||
{
|
||||
if (Pack1A1Data == null)
|
||||
{
|
||||
return ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
}
|
||||
return GetEventEndTime(Pack1A1Data.lastID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
namespace GameCore
|
||||
{
|
||||
public class Pack1A2Data
|
||||
{
|
||||
public Pack1A2Data(int ID,
|
||||
//活动id
|
||||
int eventId,
|
||||
int vipLevel)
|
||||
{
|
||||
this.ID = ID;
|
||||
this.lastID = eventId;
|
||||
this.index = 0;
|
||||
this.vipLevel = vipLevel;
|
||||
}
|
||||
public int ID;
|
||||
//领取第几个
|
||||
public int index;
|
||||
public int lastID;
|
||||
//触发当时VIP等级
|
||||
public int vipLevel;
|
||||
public int rodDstId;
|
||||
public int rodSrcId;
|
||||
}
|
||||
public partial class FishingEventData
|
||||
{
|
||||
string Pack1A2DataKey = "Pack1A2Data";
|
||||
public Pack1A2Data Pack1A2Data;
|
||||
void GiftFace1A2(EventPackManager packManager, int eventID)
|
||||
{
|
||||
if (Pack1A2Data == null)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(eventID, UITypes.GiftPopupPanel_11, packManager.PackType);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<int> packIDs = packManager.VIPPackList[Pack1A2Data.vipLevel];
|
||||
if (Pack1A2Data.lastID != eventID || packIDs.Count > Pack1A2Data.index)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(eventID, UITypes.GiftPopupPanel_11, packManager.PackType);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public void SetPack1A2(FishingEvent t)
|
||||
{
|
||||
if (Pack1A2Data == null)
|
||||
{
|
||||
Pack1A2Data = ReadEventArchive<Pack1A2Data>(Pack1A2DataKey);
|
||||
}
|
||||
EventPackManager eventPackDataRead = _tables.TbEventPackManager.DataMap[t.RedirectID];
|
||||
|
||||
GiftFace1A2(eventPackDataRead, t.ID);
|
||||
if (Pack1A2Data != null && Pack1A2Data.lastID == t.ID)
|
||||
{
|
||||
SetType();
|
||||
SetChainPackRed();
|
||||
return;
|
||||
}
|
||||
|
||||
Pack1A2Data = new Pack1A2Data(
|
||||
eventPackDataRead.ID,
|
||||
t.ID,//活动id
|
||||
GContext.container.Resolve<PlayerData>().PriceLv
|
||||
);
|
||||
SetType();
|
||||
SetRodDstId();
|
||||
SetChainPackRed();
|
||||
playerSaveStore.Write(Pack1A2DataKey, JsonMapper.ToJson(Pack1A2Data)).RequireAccepted();
|
||||
}
|
||||
void SetType()
|
||||
{
|
||||
EventPackManager eventPackManager = Get1A2EventPackManager();
|
||||
if (eventPackManager != null)
|
||||
{
|
||||
UITypes.GiftPopupPanel_11.SetType(eventPackManager.Panel);
|
||||
}
|
||||
}
|
||||
void SetRodDstId()
|
||||
{
|
||||
Pack1A2Data.rodSrcId = 330040000;
|
||||
Item item = _tables.TbItem.GetOrDefault(Pack1A2Data.rodSrcId);
|
||||
Drop drop = _tables.TbDrop.GetOrDefault(item.RedirectID);
|
||||
List<int> rodIDs = drop.DropList.DropIDList;
|
||||
int level = -1;
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
for (int i = 0; i < rodIDs.Count; i++)
|
||||
{
|
||||
int curLevel = playerFishData.GetRodLevel(rodIDs[i]);
|
||||
if (curLevel > level)
|
||||
{
|
||||
level = curLevel;
|
||||
Pack1A2Data.rodDstId = rodIDs[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
public void SetPack1A2Index()
|
||||
{
|
||||
if (Pack1A2Data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Pack1A2Data.index++;
|
||||
playerSaveStore.Write(Pack1A2DataKey, JsonMapper.ToJson(Pack1A2Data)).RequireAccepted();
|
||||
SetChainPackRed();
|
||||
}
|
||||
void SetChainPackRed()
|
||||
{
|
||||
bool isRed = false;
|
||||
EventPackManager eventPackManager = Get1A2EventPackManager();
|
||||
List<int> packIDs = eventPackManager.VIPPackList[Pack1A2Data.vipLevel];
|
||||
if (packIDs.Count > Pack1A2Data.index)
|
||||
{
|
||||
var pack = _tables.TbPack.GetOrDefault(packIDs[Pack1A2Data.index]);
|
||||
isRed = _tables.TbIAPItemList.GetOrDefault(pack.IAPID) == null;
|
||||
}
|
||||
else
|
||||
{
|
||||
//购买完 特殊处理
|
||||
GContext.Publish(new TargetEvent(Pack1A2Data.lastID, 6, 1));
|
||||
}
|
||||
RedPointManager.Instance.SetRedPointState(HomeBtnOneTwoPack.redKey, isRed);
|
||||
}
|
||||
public int GetPack1A2Index()
|
||||
{
|
||||
if (Pack1A2Data == null)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
return Pack1A2Data.index;
|
||||
}
|
||||
public EventPackManager Get1A2EventPackManager()
|
||||
{
|
||||
if (Pack1A2Data == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return _tables.TbEventPackManager.DataMap[Pack1A2Data.ID];
|
||||
}
|
||||
public DateTime GetEvent1A2EndTime()
|
||||
{
|
||||
if (Pack1A2Data == null)
|
||||
{
|
||||
return ZZTimeHelper.UtcNow().UtcNowOffset(); ;
|
||||
}
|
||||
return GetEventEndTime(Pack1A2Data.lastID);
|
||||
}
|
||||
public List<ItemData> Get1A2ItemDatasBuyPack(int dropID)
|
||||
{
|
||||
List<ItemData> itemDatas = GContext.container.Resolve<PlayerItemData>().GetItemDataByDropIdLureInflation(null, dropID);
|
||||
for (int i = 0; i < itemDatas.Count; i++)
|
||||
{
|
||||
if (itemDatas[i].id == Pack1A2Data.rodSrcId)
|
||||
{
|
||||
itemDatas[i].id = Pack1A2Data.rodDstId;
|
||||
}
|
||||
}
|
||||
return itemDatas;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
namespace GameCore
|
||||
{
|
||||
public partial class FishingEventData : IDisposable
|
||||
{
|
||||
// void GetTransitionDataPVP(int subType = 0)
|
||||
// {
|
||||
// FishingEvent fishingEvent = null;
|
||||
// if (transitionDataDic != null)
|
||||
// {
|
||||
// foreach (var item in transitionDataDic)
|
||||
// {
|
||||
// FishingEvent fe = _tables.TbFishingEvent.GetOrDefault(item.Key);
|
||||
// if (fe != null && (subType == fe.SubType || subType == 0) && fe.Type == 5 && !Condition(fe))
|
||||
// {
|
||||
// fishingEvent = fe;
|
||||
// int count = transitionDataDic[fishingEvent.ID];
|
||||
// if (count > 0)
|
||||
// {
|
||||
// PvpEventCycleItem cycle = _tables.TbPvpEventCycleItem.
|
||||
// DataList.Find(x => x.Type == fe.Type && x.SubType == fe.SubType);
|
||||
// if (cycle != null)
|
||||
// {
|
||||
// SetTransitionData(fishingEvent.ID, cycle.EventName_l10n_key, cycle.ItemId, cycle.EventEndSettlement);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// transitionDataDic.Remove(fishingEvent.ID);
|
||||
// SaveTransitionData();
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
public int GetInitWelcomeGiftPVP(int eventID)
|
||||
{
|
||||
int welcomeGift = 0;
|
||||
if (eventID > 0)
|
||||
{
|
||||
FishingEvent fe = _tables.TbFishingEvent.GetOrDefault(eventID);
|
||||
if (fe != null)
|
||||
{
|
||||
PvpEventCycleItem cycle = _tables.TbPvpEventCycleItem.
|
||||
DataList.Find(x => x.Type == fe.Type && x.SubType == fe.SubType);
|
||||
if (cycle != null)
|
||||
{
|
||||
welcomeGift = cycle.WelcomeGift;
|
||||
}
|
||||
}
|
||||
}
|
||||
return welcomeGift;
|
||||
}
|
||||
public int GetRedDotPVP(int eventID)
|
||||
{
|
||||
int redDot = 10;
|
||||
if (eventID > 0)
|
||||
{
|
||||
FishingEvent fe = _tables.TbFishingEvent.GetOrDefault(eventID);
|
||||
if (fe != null)
|
||||
{
|
||||
PvpEventCycleItem cycle = _tables.TbPvpEventCycleItem.
|
||||
DataList.Find(x => x.Type == fe.Type && x.SubType == fe.SubType);
|
||||
if (cycle != null)
|
||||
{
|
||||
redDot = cycle.RedDot;
|
||||
}
|
||||
}
|
||||
}
|
||||
return redDot;
|
||||
}
|
||||
#region SoloData
|
||||
public DuelData duelData { get; private set; }
|
||||
int rankTierChangeState = 0;
|
||||
EventSolomain curSolomain;
|
||||
public int PVPToken { private set; get; }//pvp商店代币
|
||||
|
||||
public void SetRankTierChange(EventSolomain beforeSolomain, EventSolomain eventSolomain)
|
||||
{
|
||||
rankTierChangeState = beforeSolomain.RankTier > eventSolomain.RankTier ? 1 : 2;
|
||||
this.curSolomain = beforeSolomain;
|
||||
}
|
||||
public async void ShowRankTierChange()
|
||||
{
|
||||
var store = playerSaveStore;
|
||||
if (!store.IsCurrent) return;
|
||||
if (rankTierChangeState > 0 && curSolomain != null)
|
||||
{
|
||||
GameObject go = await UIManager.Instance.ShowUI(UITypes.EventFishingDuelRankChangePopupPanel);
|
||||
if (!store.IsCurrent) return;
|
||||
EventFishingDuelRankChangePopupPanel changePopupPanel = go.GetComponent<EventFishingDuelRankChangePopupPanel>();
|
||||
changePopupPanel.SetRank(rankTierChangeState == 1, curSolomain);
|
||||
rankTierChangeState = 0;
|
||||
curSolomain = null;
|
||||
}
|
||||
}
|
||||
public void SoloCheckClaimReward()
|
||||
{
|
||||
var loaded = ReadEventArchive<DuelData>(FishingDuelManager.SoloDataKey);
|
||||
if (loaded != null)
|
||||
{
|
||||
duelData = loaded;
|
||||
int fishingEventID = GContext.container.Resolve<FishingEventData>().GetEvent(5, 1);
|
||||
if (duelData.rewardEventID > 0 && duelData.preProgress > 0 && duelData.rewardEventID != fishingEventID)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddFaceCustomUIData(UITypes.EventFishingDuelSettlementPanel, true);
|
||||
//_ = UIManager.Instance.ShowUI(UITypes.EventFishingDuelSettlementPanel);
|
||||
}
|
||||
//GetTransitionDataPVP(1);
|
||||
}
|
||||
}
|
||||
//领取赛季奖励
|
||||
public void ClaimReward()
|
||||
{
|
||||
if (duelData.rewardEventID > 0 && duelData.preProgress > 0)
|
||||
{
|
||||
duelData.soloTimes = 0;
|
||||
duelData.rewardEventID = 0;
|
||||
duelData.preProgress = 0;
|
||||
string json_duelData = Newtonsoft.Json.JsonConvert.SerializeObject(duelData);
|
||||
playerSaveStore.Write(FishingDuelManager.SoloDataKey, json_duelData).RequireAccepted();
|
||||
}
|
||||
}
|
||||
public void GMAddSoloProgress(int count)
|
||||
{
|
||||
if (duelData != null)
|
||||
{
|
||||
duelData.progress = count;
|
||||
SaveDuelData();
|
||||
}
|
||||
}
|
||||
public void InitSoloData(FishingEvent t)
|
||||
{
|
||||
var loaded = ReadEventArchive<DuelData>(FishingDuelManager.SoloDataKey);
|
||||
if (loaded != null)
|
||||
{
|
||||
duelData = loaded;
|
||||
}
|
||||
else
|
||||
{
|
||||
duelData = new DuelData();
|
||||
duelData.progress = _tables.TbEventSoloConfig.InitTrophy;
|
||||
}
|
||||
|
||||
if (t.ID != duelData.eventID)
|
||||
{
|
||||
duelData.eventID = t.ID;
|
||||
int min = GetInitWelcomeGiftPVP(t.ID);
|
||||
if (duelData.tickets < min)
|
||||
{
|
||||
duelData.tickets = min;
|
||||
}
|
||||
duelData.soloTimes = 0;
|
||||
duelData.rePVPToken = 0;
|
||||
duelData.buyItem.Clear();
|
||||
int reset = duelData.progress - _tables.TbEventSoloConfig.MinTrophy;
|
||||
if (reset > 0)
|
||||
{
|
||||
//duelData.progress = _tables.TbEventSoloConfig.SettlementTrophy +
|
||||
// (int)(reset * _tables.TbEventSoloConfig.SettlementTrophyMultiplier);
|
||||
|
||||
var data = _tables.TbEventSolomain.DataList;
|
||||
var solomain = data[0];
|
||||
if (duelData.progress > 0)
|
||||
{
|
||||
for (int i = data.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (duelData.progress >= data[i].TrophyRange[0])
|
||||
{
|
||||
solomain = data[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
duelData.progress -= solomain.SettleDeducTrophy;
|
||||
}
|
||||
//duelData.progress /= _tables.TbEventSoloConfig.TrophyMutiple;
|
||||
//duelData.progress *= _tables.TbEventSoloConfig.TrophyMutiple;
|
||||
if (duelData.progress < _tables.TbEventSoloConfig.MinTrophy)
|
||||
{
|
||||
duelData.progress = _tables.TbEventSoloConfig.MinTrophy;
|
||||
}
|
||||
//GContext.Publish(new TargetEvent(t.ID, 5, 1));
|
||||
SaveDuelData();
|
||||
}
|
||||
}
|
||||
public void AddDuelTickets(int tickets)
|
||||
{
|
||||
if (duelData != null)
|
||||
{
|
||||
if (tickets < 0)
|
||||
{
|
||||
duelData.soloTimes++;
|
||||
if (duelData.soloTimes >= _tables.TbEventSoloConfig.ClaimRewardMatchCount)
|
||||
{
|
||||
duelData.rewardEventID = duelData.eventID;
|
||||
duelData.preProgress = duelData.progress;
|
||||
}
|
||||
}
|
||||
duelData.tickets += tickets;
|
||||
//SaveTransitionData(duelData.eventID, duelData.tickets);
|
||||
SaveDuelData();
|
||||
}
|
||||
}
|
||||
public int GetDuelTickets()
|
||||
{
|
||||
if (duelData != null)
|
||||
{
|
||||
return duelData.tickets;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public bool BuyPVPItem(int id)
|
||||
{
|
||||
if (duelData != null && !duelData.buyItem.ContainsKey(id))
|
||||
{
|
||||
duelData.buyItem[id] = 1;
|
||||
SaveDuelData();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public bool IsBuyPVPItem(int id)
|
||||
{
|
||||
if (duelData != null && duelData.buyItem.ContainsKey(id))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public DateTime SetPVPShopTime()
|
||||
{
|
||||
DateTime dateTime = ZZTimeHelper.UtcNow().UtcNowOffset().Date;
|
||||
int offfset = (int)dateTime.DayOfWeek - 1;
|
||||
|
||||
if (offfset < 0)
|
||||
{
|
||||
offfset = -offfset;
|
||||
}
|
||||
else
|
||||
{
|
||||
offfset = 7 - offfset;
|
||||
}
|
||||
DateTime endTime = dateTime.AddDays(offfset);
|
||||
if (endTime.DayOfYear != duelData.pvpShopTimeDay)
|
||||
{
|
||||
if (duelData.pvpShopTimeDay != -1)
|
||||
{
|
||||
duelData.buyItem.Clear();
|
||||
}
|
||||
duelData.inflation = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
duelData.pvpShopTimeDay = endTime.DayOfYear;
|
||||
SaveDuelData();
|
||||
}
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void AddPVPToken(int value)
|
||||
{
|
||||
PVPToken += value;
|
||||
playerSaveStore.Write("PVPToken", PVPToken.ToString()).RequireAccepted();
|
||||
if (value < 0 && duelData != null)
|
||||
{
|
||||
duelData.rePVPToken -= value;
|
||||
SaveDuelData();
|
||||
}
|
||||
}
|
||||
public void SaveDuelData()
|
||||
{
|
||||
string json_duelData = Newtonsoft.Json.JsonConvert.SerializeObject(duelData);
|
||||
playerSaveStore.Write(FishingDuelManager.SoloDataKey, json_duelData).RequireAccepted();
|
||||
}
|
||||
#endregion SoloData
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,573 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DataCenter
|
||||
{
|
||||
// 锁链礼包
|
||||
|
||||
public class TOfferChainsData
|
||||
{
|
||||
public class TItem
|
||||
{
|
||||
public int Id;
|
||||
public int Item;
|
||||
public int Number;
|
||||
// 0 可选,1 高亮 2 糊了 ,锁链隐藏 应该转换成Enum
|
||||
public int Status;
|
||||
// public int SKUId;
|
||||
}
|
||||
public bool FirstOpen; // 是否首次打开
|
||||
public int EventId; // 默认存一下ID
|
||||
public DateTime StartTime; // 开始时间
|
||||
public DateTime EndTime;// 结束时间
|
||||
public int ItemId = -1;
|
||||
// 当前抽奖次数,必须抽了才会更新
|
||||
public int LotteryNum;
|
||||
public List<TItem> Items;
|
||||
public bool Finished;
|
||||
public int ChestReward;
|
||||
public List<int> RewardList; // 当前获得的奖励Item
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class OfferChainsChestManager
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
|
||||
public const string SaveKey = "OfferChainsChestData";
|
||||
[Inject] public Tables _tables { get; set; }
|
||||
// 计入当前Event
|
||||
private FishingEvent _fishingEvent;
|
||||
//
|
||||
public EventOfferChainsChestMain OfferChainsChestMain { get; set; }
|
||||
public List<EventOfferChainsChestRewards> OfferChainsChestRewards { get; set; }
|
||||
// 当前可保存数据
|
||||
public TOfferChainsData OfferChainsData { get; private set; }
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVOfferState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeOffer(raw,false,snapshot,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
OfferChainsData=null; ValidIds=new List<int>(); _fishingEvent=null; _isInit=false;
|
||||
OfferChainsChestMain=null; OfferChainsChestRewards=null;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVOfferState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
OfferChainsData=!state.IsPresent ? null : new TOfferChainsData
|
||||
{
|
||||
FirstOpen=state.FirstOpen,EventId=state.EventId,StartTime=state.StartTime,EndTime=state.EndTime,
|
||||
ItemId=state.ItemId,LotteryNum=state.LotteryNum,
|
||||
Items=state.Items.Select(item=>new TOfferChainsData.TItem {
|
||||
Id=item.Id,Item=item.Item,Number=item.Number,Status=item.Status }).ToList(),
|
||||
RewardList=new List<int>(state.RewardList),Finished=state.Finished,ChestReward=state.ChestReward
|
||||
};
|
||||
}
|
||||
internal bool IsInactiveForKVValidation => !_isInit && _fishingEvent == null;
|
||||
public List<int> ValidIds;
|
||||
|
||||
public bool IsFirstOpen
|
||||
{
|
||||
get => OfferChainsData.FirstOpen;
|
||||
set
|
||||
{
|
||||
if (OfferChainsData.FirstOpen)
|
||||
{
|
||||
OfferChainsData.FirstOpen = false;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void UpdateEventData(FishingEvent fishingEvent)
|
||||
{
|
||||
ELog($"UpdateEventData - > {fishingEvent}");
|
||||
_fishingEvent = fishingEvent;
|
||||
UpdateLocalData();
|
||||
}
|
||||
private void UpdateLocalData()
|
||||
{
|
||||
//选择当前的 Main
|
||||
var redirectId = _fishingEvent.RedirectID;
|
||||
var configList = _tables.TbEventOfferChainsChestMain.DataList;
|
||||
foreach (var eventChallenge in configList.Where(eventChallenge => eventChallenge.ID == redirectId))
|
||||
{
|
||||
OfferChainsChestMain = eventChallenge;
|
||||
break;
|
||||
}
|
||||
OfferChainsChestRewards = _tables.TbEventOfferChainsChestRewards.DataList;
|
||||
// SkuIds = OfferChainsChestMain.IapList;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private bool _isInit = false;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
ELog("Init-> ");
|
||||
|
||||
if (_fishingEvent == null)
|
||||
return;
|
||||
//-
|
||||
if (_fishingEvent.ID != OfferChainsData?.EventId)
|
||||
{
|
||||
ClearOfferChainsData();
|
||||
}
|
||||
|
||||
if (_isInit)
|
||||
return;
|
||||
|
||||
if (OfferChainsData == null)
|
||||
{
|
||||
if (_fishingEvent == null) // 不存在Event
|
||||
{
|
||||
return;
|
||||
}
|
||||
InitOfferChainsData();
|
||||
// CreateNewData();
|
||||
}
|
||||
UpdateOfferChainsData();
|
||||
//UpdateData()
|
||||
_isInit = true;
|
||||
ELog("初始化完成-> ");
|
||||
}
|
||||
//
|
||||
private void ProcessLastOne()
|
||||
{
|
||||
ELog("ProcessLastOne-> ");
|
||||
if (OfferChainsData != null)
|
||||
{
|
||||
Settle();
|
||||
// if (!IsTimeAllow())
|
||||
// {
|
||||
// Settle();
|
||||
// // ClearOfferChainsData();
|
||||
// }
|
||||
// // else if (IsCanGetFinalReward())
|
||||
// else
|
||||
// {
|
||||
// Settle(); // 补充一下最终大奖
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateOfferChainsData()
|
||||
{
|
||||
ELog("UpdateOfferChainsData");
|
||||
// 更新可用数据
|
||||
UpdateTime();
|
||||
UpdateOfferChainsItems();
|
||||
UpdateValidIds();
|
||||
GetLotteryItemId();
|
||||
//-
|
||||
if (_fishingEvent != null && OfferChainsData != null)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(_fishingEvent.ID, new UIType(OfferChainsChestMain.UIPanel), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateValidIds()
|
||||
{
|
||||
var lotteryNum = OfferChainsData.LotteryNum;
|
||||
ValidIds = (from item in OfferChainsData?.Items where item.Status == 0 select item.Id).ToList();
|
||||
}
|
||||
|
||||
|
||||
private void InitOfferChainsData()
|
||||
{
|
||||
var limitedTime = (LimitedTime)_fishingEvent.TimeDefinition;
|
||||
var startTime = GlobalUtils.TryParseDateTime(limitedTime.StartTime, ZZTimeHelper.UtcNow());
|
||||
var endTime = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow());
|
||||
|
||||
|
||||
OfferChainsData = new TOfferChainsData
|
||||
{
|
||||
FirstOpen = true,
|
||||
EventId = _fishingEvent.ID,
|
||||
StartTime = startTime,
|
||||
EndTime = endTime,
|
||||
Items = new List<TOfferChainsData.TItem>(),
|
||||
RewardList = new List<int>(),
|
||||
ChestReward = OfferChainsChestMain.ChestReward, // 最终大奖存储下,方便额外的处理
|
||||
};
|
||||
|
||||
for (var i = 0; i < OfferChainsChestRewards.Count; ++i)
|
||||
{
|
||||
var item = new TOfferChainsData.TItem
|
||||
{
|
||||
Id = OfferChainsChestRewards[i].ID,
|
||||
Item = OfferChainsChestRewards[i].Item,
|
||||
Number = OfferChainsChestRewards[i].Number,
|
||||
Status = 0,
|
||||
// SKUId = OfferChainsChestMain.IapList[i],
|
||||
};
|
||||
OfferChainsData.Items.Add(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public bool IsTimeAllow()
|
||||
{
|
||||
var now = ZZTimeHelper.UtcNow();
|
||||
|
||||
var startTime = OfferChainsData.StartTime;
|
||||
var endTime = OfferChainsData.EndTime;
|
||||
#if UNITY_EDITOR
|
||||
var timeOpened = (now - startTime).TotalSeconds;
|
||||
var timeEnded = (endTime - now).TotalSeconds;
|
||||
ELog($"StartTime: {startTime} Now: {now} EndTime:{endTime} TimeOpened:{timeOpened} TimeToEnd:{timeEnded} Other: {1800000 - timeOpened} IsTimeAllow => {now <= endTime}");
|
||||
#endif
|
||||
return now >= startTime && now <= endTime;
|
||||
}
|
||||
public void CheckInit()
|
||||
{
|
||||
ELog("CheckInit -> ");
|
||||
|
||||
ProcessLastOne();
|
||||
Init();
|
||||
}
|
||||
|
||||
// ReSharper disable Unity.PerformanceAnalysis
|
||||
private static void Log(object t)
|
||||
{
|
||||
Debug.Log($"<color=yellow>OfferChainsChestManager -> {t} </color>");
|
||||
}
|
||||
|
||||
private static void ELog(object t)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"<color=yellow>OfferChainsChestManager -> {t} </color>");
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool CheckOpen()
|
||||
{
|
||||
//
|
||||
if (OfferChainsData == null) return false;
|
||||
//
|
||||
if (!IsTimeAllow()) return false;
|
||||
|
||||
if (OfferChainsChestMain == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsFinished())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsFinished()
|
||||
{
|
||||
return OfferChainsData?.Finished ?? false;
|
||||
}
|
||||
|
||||
private void SaveData()
|
||||
{
|
||||
ELog($"SaveData-> {JsonConvert.SerializeObject(OfferChainsData)}");
|
||||
playerSaveStore.Write(SaveKey, JsonConvert.SerializeObject(OfferChainsData)).RequireAccepted();
|
||||
}
|
||||
|
||||
public void LoadData(string dataValue)
|
||||
{
|
||||
ELog($"LoadData -> {dataValue}");
|
||||
OfferChainsData = JsonConvert.DeserializeObject<TOfferChainsData>(dataValue);
|
||||
}
|
||||
|
||||
// 进入游戏舞台
|
||||
public async void OnEnterGameAct()
|
||||
{
|
||||
if (!playerSaveStore.IsCurrent) return;
|
||||
Log("OnEnterGameAct()");
|
||||
IsFirstOpen = false;
|
||||
await UIManager.Instance.ShowUI(new UIType(OfferChainsChestMain.UIPanel));
|
||||
}
|
||||
|
||||
public static T WeightedRandom<T>(Dictionary<T, int> weightTable)
|
||||
{
|
||||
// 1. 计算总权重
|
||||
var totalWeight = weightTable.Values.Sum();
|
||||
// 2. 生成随机数
|
||||
var randomValue = UnityEngine.Random.Range(0, totalWeight);
|
||||
|
||||
// 3. 线性遍历选择
|
||||
var currentWeight = 0;
|
||||
foreach (var (key, value) in weightTable)
|
||||
{
|
||||
currentWeight += value;
|
||||
if (randomValue < currentWeight)
|
||||
return key;
|
||||
}
|
||||
return default; // 理论上不会执行到这里
|
||||
}
|
||||
|
||||
// 活动当前的抽奖索引
|
||||
private int CalcLotteryItemId()
|
||||
{
|
||||
var lotteryNum = OfferChainsData.LotteryNum;
|
||||
if (ValidIds.Count <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (OfferChainsChestRewards == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// while (true)
|
||||
// {
|
||||
var dict = OfferChainsChestRewards.Where(
|
||||
item => ValidIds.Contains(item.ID) && item.LimitConfig <= lotteryNum)
|
||||
.ToDictionary(elem => (elem.ID, elem.LimitConfig), elem => elem.Weight);
|
||||
var (elemId, limitConfig) = WeightedRandom(dict);
|
||||
Log($"CalcLotteryItemId -> {lotteryNum} {elemId} {limitConfig}");
|
||||
// if (limitConfig >0 && lotteryNum > limitConfig )
|
||||
return elemId;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
public int GetLotteryItemId()
|
||||
{
|
||||
ELog($"GetLotteryItemId -> {OfferChainsData.ItemId}");
|
||||
if (OfferChainsData.ItemId == -1 || !ValidIds.Contains(OfferChainsData.ItemId))
|
||||
{
|
||||
UpdateLotteryItemId();
|
||||
}
|
||||
return OfferChainsData.ItemId;
|
||||
}
|
||||
|
||||
private void UpdateOfferChainsItems()
|
||||
{
|
||||
if (OfferChainsData != null && _fishingEvent != null && OfferChainsData.EventId == _fishingEvent.ID)
|
||||
{
|
||||
var items = OfferChainsData.Items;
|
||||
if (OfferChainsChestRewards is { Count: > 0 })
|
||||
{
|
||||
for (int i = 0; i < items.Count; ++i)
|
||||
{
|
||||
EventOfferChainsChestRewards reward = null;
|
||||
if (i < OfferChainsChestRewards.Count)
|
||||
{
|
||||
reward = OfferChainsChestRewards[i];
|
||||
}
|
||||
if (reward != null)
|
||||
{
|
||||
items[i].Id = reward.ID;
|
||||
items[i].Item = reward.Item;
|
||||
items[i].Number = reward.Number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (OfferChainsChestMain != null)
|
||||
{
|
||||
OfferChainsData.ChestReward = OfferChainsChestMain.ChestReward;
|
||||
}
|
||||
}
|
||||
|
||||
// for (var i = 0; i < OfferChainsChestRewards.Count; ++i)
|
||||
// {
|
||||
// var item = new TOfferChainsData.TItem
|
||||
// {
|
||||
// Id = OfferChainsChestRewards[i].ID,
|
||||
// Item = OfferChainsChestRewards[i].Item,
|
||||
// Number = OfferChainsChestRewards[i].Number,
|
||||
// Status = 0,
|
||||
// };
|
||||
// OfferChainsData.Items.Add(item);
|
||||
// }
|
||||
}
|
||||
|
||||
private void UpdateTime()
|
||||
{
|
||||
if (OfferChainsData != null && _fishingEvent != null && OfferChainsData.EventId == _fishingEvent.ID)
|
||||
{
|
||||
var limitedTime = (LimitedTime)_fishingEvent.TimeDefinition;
|
||||
var startTime = GlobalUtils.TryParseDateTime(limitedTime.StartTime, ZZTimeHelper.UtcNow());
|
||||
var endTime = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow().AddDays(-1));
|
||||
OfferChainsData.StartTime = startTime;
|
||||
OfferChainsData.EndTime = endTime;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLotteryItemId()
|
||||
{
|
||||
var targetId = CalcLotteryItemId();
|
||||
ELog($"UpdateLotteryItemId -> {OfferChainsData.ItemId} => {targetId}");
|
||||
OfferChainsData.ItemId = targetId;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
//- 结束当前
|
||||
public void FinishLottery(int targetId)
|
||||
{
|
||||
Log($"FinishLottery -> {targetId}");
|
||||
var lastItem = OfferChainsData.Items.SingleOrDefault(it => it.Id == targetId);
|
||||
if (lastItem is { Status: not 2 })
|
||||
{
|
||||
lastItem.Status = 2;
|
||||
}
|
||||
}
|
||||
|
||||
//- 开始下一轮数据
|
||||
public void UpdateForNextLottery()
|
||||
{
|
||||
UpdateValidIds();
|
||||
UpdateLotteryItemId();
|
||||
}
|
||||
|
||||
public bool IsCanGetFinalReward()
|
||||
{
|
||||
var ret = OfferChainsData.LotteryNum >= OfferChainsData.Items.Count
|
||||
&& !OfferChainsData.Finished;
|
||||
|
||||
ELog($"IsCanGetFinalReward {ret} : {OfferChainsData.LotteryNum} {OfferChainsData.Items.Count} {OfferChainsData.Finished}");
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void ClearOfferChainsData()
|
||||
{
|
||||
OfferChainsData = null;
|
||||
_isInit = false;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public TOfferChainsData.TItem GetTItemById(int targetId)
|
||||
{
|
||||
var lastItem = OfferChainsData.Items.FirstOrDefault(it => it.Id == targetId);
|
||||
return lastItem;
|
||||
}
|
||||
|
||||
public int GetDropIdById(int id)
|
||||
{
|
||||
var item = GetTItemById(id);
|
||||
var itemData = _tables.TbItem.GetOrDefault(item.Item);
|
||||
return itemData.RedirectID;
|
||||
}
|
||||
public void OnGetFinalReward()
|
||||
{
|
||||
ELog("OnGetFinalReward()-> ");
|
||||
|
||||
// var item = _tables.TbItem.GetOrDefault(OfferChainsChestMain.ChestReward);
|
||||
var item = _tables.TbItem.GetOrDefault(OfferChainsData.ChestReward);
|
||||
var itemFinal = GContext.container.Resolve<PlayerItemData>()
|
||||
.GetItemDropItemList(item.RedirectID, 1);
|
||||
GContext.container.Resolve<PlayerItemData>().LureInflation(itemFinal, null);
|
||||
GContext.container.Resolve<PlayerItemData>().AddItem(itemFinal, null);
|
||||
GContext.Publish(new ShowData(itemFinal));
|
||||
GContext.Publish(new ShowData());
|
||||
OfferChainsData.Finished = true;
|
||||
SaveData();
|
||||
GContext.Publish(new OfferChainsRefreshEvent());
|
||||
}
|
||||
public float GetProbability(int id)
|
||||
{
|
||||
var theIdWeight = OfferChainsChestRewards.FirstOrDefault(item => item.ID == id)?.Weight ?? 0;
|
||||
var weightTable = OfferChainsChestRewards
|
||||
.Where(item => !IsItemFinished(item.ID))
|
||||
.Select(item => item.Weight).ToList();
|
||||
var totalWeight = weightTable.Sum();
|
||||
return (float)Math.Round((float)theIdWeight / totalWeight * 100, 2);
|
||||
}
|
||||
|
||||
private bool IsItemFinished(int itemID)
|
||||
{
|
||||
var lastItem = OfferChainsData.Items.SingleOrDefault(it => it.Id == itemID);
|
||||
return lastItem?.Status == 2;
|
||||
}
|
||||
|
||||
public TimeSpan GetTimeRemain()
|
||||
{
|
||||
if (OfferChainsData != null)
|
||||
{
|
||||
return OfferChainsData.EndTime - ZZTimeHelper.UtcNow();
|
||||
}
|
||||
// 活动未开始
|
||||
return ZZTimeHelper.UtcNow().AddDays(-1) - ZZTimeHelper.UtcNow();
|
||||
}
|
||||
public void Settle()
|
||||
{
|
||||
ELog("Settle");
|
||||
if (IsCanGetFinalReward())
|
||||
{
|
||||
OnGetFinalReward();
|
||||
}
|
||||
}
|
||||
|
||||
public bool UIRunning { get; set; } = false;
|
||||
public void OnBuySuccess()
|
||||
{
|
||||
Log("OnBuySuccess -> ");
|
||||
OfferChainsData.RewardList.Add(OfferChainsData.ItemId);
|
||||
OfferChainsData.LotteryNum += 1;
|
||||
// 为了处理打点
|
||||
if (IsCanGetFinalReward())
|
||||
{
|
||||
OfferChainsData.RewardList.Add(OfferChainsData.ChestReward);
|
||||
}
|
||||
FinishLottery(OfferChainsData.ItemId);
|
||||
SaveData(); // -存
|
||||
EventTracking();
|
||||
|
||||
|
||||
//NEXT TURN
|
||||
if (UIRunning)
|
||||
{ // 播放动画逻辑,交给UI
|
||||
return;
|
||||
}
|
||||
UpdateForNextLottery();
|
||||
Settle();
|
||||
}
|
||||
|
||||
public void EventTracking()
|
||||
{
|
||||
//var price = GetPayAmountById(OfferChainsData.ItemId);
|
||||
var price = GetIapItemListThisTime(OfferChainsData.LotteryNum - 1)?.PaymentAmount ?? 0F;
|
||||
var itemEnt = GetTItemById(OfferChainsData.ItemId);
|
||||
var rewardStr = string.Join(',', OfferChainsData.RewardList);
|
||||
var number = OfferChainsData.LotteryNum;
|
||||
#if AGG
|
||||
using (var e = GEvent.TackEvent("offer_chainchest"))
|
||||
{
|
||||
e.AddContent("event_id", OfferChainsData.EventId);
|
||||
e.AddContent("number", number);
|
||||
e.AddContent("price", price);
|
||||
e.AddContent("reward", OfferChainsData.ItemId);
|
||||
e.AddContent("reward_claimed_list", rewardStr);
|
||||
}
|
||||
#endif
|
||||
Log($"EventTracking: event_id: {OfferChainsData.EventId} number: {number} price: {price} " +
|
||||
$"reward :{OfferChainsData.ItemId} reward_claimed_list :{rewardStr}");
|
||||
}
|
||||
|
||||
public IAPItemList GetIapItemListThisTime()
|
||||
{
|
||||
return GetIapItemListThisTime(OfferChainsData.LotteryNum);
|
||||
}
|
||||
|
||||
private IAPItemList GetIapItemListThisTime(int index)
|
||||
{
|
||||
// var index = OfferChainsData.LotteryNum;
|
||||
index = Mathf.Clamp(index, 0, OfferChainsChestMain.IapList.Count - 1);
|
||||
var iapId = OfferChainsChestMain.IapList[index];
|
||||
var iAPItemList = _tables.TbIAPItemList.GetOrDefault(iapId);
|
||||
return iAPItemList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,588 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DataCenter
|
||||
{
|
||||
// 数据保存
|
||||
public enum TItemStatues
|
||||
{
|
||||
Valid = 0,
|
||||
HighLight = 1,
|
||||
Done = 2,
|
||||
}
|
||||
public class TOfferDiscoPartyData
|
||||
{
|
||||
public class TItem
|
||||
{
|
||||
public int Id;
|
||||
public int Item;
|
||||
public int Number;
|
||||
|
||||
// 0 可选,1 高亮 2 糊了 ,锁链隐藏 应该转换成Enum
|
||||
public TItemStatues Status;
|
||||
}
|
||||
|
||||
public bool FirstOpen; // 是否首次打开
|
||||
public int EventId; // 默认存一下ID
|
||||
public DateTime StartTime; // 开始时间
|
||||
public DateTime EndTime; // 结束时间
|
||||
public int ItemId = -1;
|
||||
public int LotteryNum;
|
||||
// public bool Finished;
|
||||
|
||||
// 这个看看能否删掉,只保留状态
|
||||
public List<TItem> Items;
|
||||
public List<int> RewardList; // 当前获得的奖励Item
|
||||
}
|
||||
|
||||
// Disco Manager
|
||||
public class OfferDiscoPartyManager
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
public const string SaveKey = "OfferDiscoPartyManager";
|
||||
|
||||
// 免费抽奖次数
|
||||
public int FreeTime = 1;
|
||||
|
||||
[Inject] public Tables _tables { get; set; }
|
||||
|
||||
#region 数据
|
||||
|
||||
private FishingEvent _fishingEvent;
|
||||
|
||||
public EventOfferDiscoParty OfferDiscoParty { get; private set; }
|
||||
|
||||
public List<EventOfferDiscoPartyReward> OfferDiscoPartyRewards { get; private set; }
|
||||
|
||||
public TOfferDiscoPartyData OfferDiscoPartyData { get; private set; }
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVOfferState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeOffer(raw,true,snapshot,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
OfferDiscoPartyData=null; ValidIds=new List<int>(); _fishingEvent=null; _isInit=false;
|
||||
OfferDiscoParty=null; OfferDiscoPartyRewards=null; FreeTime=1;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVOfferState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
OfferDiscoPartyData=!state.IsPresent ? null : new TOfferDiscoPartyData
|
||||
{
|
||||
FirstOpen=state.FirstOpen,EventId=state.EventId,StartTime=state.StartTime,EndTime=state.EndTime,
|
||||
ItemId=state.ItemId,LotteryNum=state.LotteryNum,
|
||||
Items=state.Items.Select(item=>new TOfferDiscoPartyData.TItem {
|
||||
Id=item.Id,Item=item.Item,Number=item.Number,Status=(TItemStatues) item.Status }).ToList(),
|
||||
RewardList=new List<int>(state.RewardList)
|
||||
};
|
||||
}
|
||||
internal bool IsInactiveForKVValidation => !_isInit && _fishingEvent == null;
|
||||
|
||||
|
||||
public List<int> ValidIds; //
|
||||
|
||||
#endregion
|
||||
|
||||
public bool IsFirstOpen
|
||||
{
|
||||
get => OfferDiscoPartyData?.FirstOpen?? false;
|
||||
set
|
||||
{
|
||||
if (OfferDiscoPartyData?.FirstOpen??false)
|
||||
{
|
||||
OfferDiscoPartyData.FirstOpen = false;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateEventData(FishingEvent fishingEvent)
|
||||
{
|
||||
ELog($"UpdateEventData - > {fishingEvent}");
|
||||
_fishingEvent = fishingEvent;
|
||||
UpdateLocalData();
|
||||
}
|
||||
|
||||
private void UpdateLocalData()
|
||||
{
|
||||
//选择当前的 Main
|
||||
var redirectId = _fishingEvent.RedirectID;
|
||||
var configList = _tables.TbEventOfferDiscoParty.DataList;
|
||||
foreach (var eventChallenge in configList.Where(eventChallenge => eventChallenge.ID == redirectId))
|
||||
{
|
||||
OfferDiscoParty = eventChallenge;
|
||||
break;
|
||||
}
|
||||
|
||||
FreeTime = OfferDiscoParty.Free;
|
||||
|
||||
var rewardList = OfferDiscoParty.RewardList;
|
||||
// OfferDiscoPartyRewards = _tables.TbEventOfferDiscoPartyReward.DataList;
|
||||
var list = _tables.TbEventOfferDiscoPartyReward.DataList;
|
||||
//TODO:LF 这个筛选未应用到程序中,看策划的应用
|
||||
//
|
||||
OfferDiscoPartyRewards = list.Where(item => rewardList.Contains(item.ID)).ToList();
|
||||
OfferDiscoPartyRewards.Sort((a,b) => a.ID.CompareTo(b.ID));
|
||||
|
||||
//添加拍脸
|
||||
|
||||
// _tables.TbEventOfferDiscoParty
|
||||
}
|
||||
|
||||
private bool _isInit = false;
|
||||
public void Init()
|
||||
{
|
||||
ELog("Init-> ");
|
||||
if (_fishingEvent == null)
|
||||
return;
|
||||
|
||||
if (_fishingEvent.ID != OfferDiscoPartyData?.EventId)
|
||||
{
|
||||
ClearOfferDiscoPartyData();
|
||||
}
|
||||
|
||||
if (_isInit)
|
||||
return;
|
||||
|
||||
|
||||
if (OfferDiscoPartyData == null)
|
||||
{
|
||||
if (_fishingEvent == null) // 不存在Event
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InitOfferDiscoPartyData();
|
||||
}
|
||||
UpdateOfferDiscoPartyData();
|
||||
_isInit = true;
|
||||
ELog("初始化完成-> ");
|
||||
}
|
||||
|
||||
private void ProcessLastOne()
|
||||
{
|
||||
if (OfferDiscoPartyData != null)
|
||||
{
|
||||
Settle();
|
||||
// if (!IsTimeAllow())
|
||||
// {
|
||||
// Settle();
|
||||
// ClearOfferDiscoPartyData();
|
||||
// }
|
||||
// else if (IsFinished())
|
||||
// {
|
||||
// Settle(); // 补充一下最终大奖
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void InitOfferDiscoPartyData()
|
||||
{
|
||||
var limitedTime = (LimitedTime)_fishingEvent.TimeDefinition;
|
||||
var startTime = GlobalUtils.TryParseDateTime(limitedTime.StartTime, ZZTimeHelper.UtcNow());
|
||||
var endTime = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow());
|
||||
|
||||
|
||||
OfferDiscoPartyData = new TOfferDiscoPartyData
|
||||
{
|
||||
FirstOpen = true,
|
||||
EventId = _fishingEvent.ID,
|
||||
StartTime = startTime,
|
||||
EndTime = endTime,
|
||||
Items = new List<TOfferDiscoPartyData.TItem>(),
|
||||
RewardList = new List<int>()
|
||||
};
|
||||
|
||||
for (var i = 0; i < OfferDiscoPartyRewards.Count; ++i)
|
||||
{
|
||||
var item = new TOfferDiscoPartyData.TItem
|
||||
{
|
||||
Id = OfferDiscoPartyRewards[i].ID,
|
||||
Item = OfferDiscoPartyRewards[i].Item,
|
||||
Number = OfferDiscoPartyRewards[i].Number,
|
||||
Status = 0,
|
||||
};
|
||||
OfferDiscoPartyData.Items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateOfferDiscoPartyData()
|
||||
{
|
||||
ELog("UpdateOfferDiscoPartyData");
|
||||
// 更新可用数据
|
||||
OfferDiscoPartyData.EventId = _fishingEvent?.ID ?? OfferDiscoPartyData.EventId;
|
||||
|
||||
UpdateTime();
|
||||
UpdateOfferDiscoPartyItems();
|
||||
UpdateValidIds();
|
||||
// UpdateLotteryItemId();
|
||||
GetLotteryItemId();
|
||||
|
||||
if (_fishingEvent != null)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(_fishingEvent.ID, new UIType(OfferDiscoParty.UIPanel), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTimeAllow()
|
||||
{
|
||||
var now = ZZTimeHelper.UtcNow();
|
||||
|
||||
var startTime = OfferDiscoPartyData.StartTime;
|
||||
var endTime = OfferDiscoPartyData.EndTime;
|
||||
#if UNITY_EDITOR
|
||||
var timeOpened = (now - startTime).TotalSeconds;
|
||||
var timeEnded = (endTime - now).TotalSeconds;
|
||||
ELog(
|
||||
$"StartTime: {startTime} Now: {now} EndTime:{endTime} TimeOpened:{timeOpened} TimeToEnd:{timeEnded} Other: {1800000 - timeOpened} IsTimeAllow => {now <= endTime}");
|
||||
#endif
|
||||
return now >= startTime && now <= endTime;
|
||||
}
|
||||
|
||||
public void CheckInit()
|
||||
{
|
||||
ELog("CheckInit-> ");
|
||||
ProcessLastOne();
|
||||
Init();
|
||||
}
|
||||
|
||||
private static void Log(object t)
|
||||
{
|
||||
Debug.Log($"<color=orange>OfferDiscoPartyManager -> {t} </color>");
|
||||
}
|
||||
private static void ELog(object t)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"<color=yellow>OfferDiscoPartyManager -> {t} </color>");
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool CheckOpen()
|
||||
{
|
||||
//
|
||||
if (OfferDiscoPartyData == null) return false;
|
||||
//
|
||||
if (!IsTimeAllow()) return false;
|
||||
|
||||
if (OfferDiscoParty == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsFinished())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsFinished()
|
||||
{
|
||||
// return OfferDiscoPartyData?.Finished ?? false;
|
||||
return OfferDiscoPartyData.LotteryNum >= OfferDiscoPartyData.Items.Count;
|
||||
}
|
||||
|
||||
private void SaveData()
|
||||
{
|
||||
playerSaveStore.Write(SaveKey, JsonConvert.SerializeObject(OfferDiscoPartyData)).RequireAccepted();
|
||||
}
|
||||
|
||||
public void LoadData(string dataValue)
|
||||
{
|
||||
ELog($"LoadData- {dataValue}");
|
||||
|
||||
OfferDiscoPartyData = JsonConvert.DeserializeObject<TOfferDiscoPartyData>(dataValue);
|
||||
//
|
||||
// if (true)
|
||||
// {
|
||||
// OfferDiscoPartyData.EventId = 1001;
|
||||
// SaveData();
|
||||
// }
|
||||
}
|
||||
|
||||
public async void OnEnterGameAct()
|
||||
{
|
||||
if (!playerSaveStore.IsCurrent) return;
|
||||
ELog("OnEnterGameAct()");
|
||||
IsFirstOpen = false;
|
||||
await UIManager.Instance.ShowUI(new UIType(OfferDiscoParty.UIPanel));
|
||||
}
|
||||
|
||||
public static T WeightedRandom<T>(Dictionary<T, int> weightTable)
|
||||
{
|
||||
// 1. 计算总权重
|
||||
var totalWeight = weightTable.Values.Sum();
|
||||
// 2. 生成随机数
|
||||
var randomValue = UnityEngine.Random.Range(0, totalWeight);
|
||||
|
||||
// 3. 线性遍历选择
|
||||
var currentWeight = 0;
|
||||
foreach (var (key, value) in weightTable)
|
||||
{
|
||||
currentWeight += value;
|
||||
if (randomValue < currentWeight)
|
||||
return key;
|
||||
}
|
||||
|
||||
return default; // 理论上不会执行到这里
|
||||
}
|
||||
|
||||
private int CalcLotteryItemId()
|
||||
{
|
||||
// UpdateValidIds();
|
||||
var lotteryNum = OfferDiscoPartyData.LotteryNum;
|
||||
if (ValidIds.Count <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (OfferDiscoPartyRewards == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// while (true)
|
||||
// {
|
||||
var dict = OfferDiscoPartyRewards
|
||||
.Where(item => ValidIds.Contains(item.ID) && lotteryNum >= (item.LimitConfig - 1))
|
||||
.ToDictionary(elem => (elem.ID, elem.LimitConfig), elem => elem.Weight);
|
||||
|
||||
if (dict?.Count <= 0)
|
||||
return -1;
|
||||
|
||||
var (elemId, limitConfig) = WeightedRandom(dict);
|
||||
ELog($"CalcLotteryItemId -> {lotteryNum} {elemId} {limitConfig}");
|
||||
// if (limitConfig >0 && lotteryNum > limitConfig )
|
||||
return elemId;
|
||||
// }
|
||||
}
|
||||
|
||||
private void UpdateValidIds()
|
||||
{
|
||||
|
||||
ValidIds = (from item in OfferDiscoPartyData?.Items where item.Status == 0 select item.Id).ToList();
|
||||
// return lotteryNum;
|
||||
}
|
||||
|
||||
public int GetLotteryItemId()
|
||||
{
|
||||
ELog($"GetLotteryItemId -> {OfferDiscoPartyData.ItemId}");
|
||||
if (OfferDiscoPartyData.ItemId == -1 || !ValidIds.Contains(OfferDiscoPartyData.ItemId))
|
||||
{
|
||||
UpdateLotteryItemId();
|
||||
}
|
||||
return OfferDiscoPartyData.ItemId;
|
||||
}
|
||||
|
||||
private void UpdateOfferDiscoPartyItems()
|
||||
{
|
||||
ELog("UpdateOfferDiscoPartyItems-> ");
|
||||
if (OfferDiscoPartyData != null && _fishingEvent != null && OfferDiscoPartyData.EventId == _fishingEvent.ID)
|
||||
{
|
||||
var items = OfferDiscoPartyData.Items;
|
||||
if (OfferDiscoPartyRewards is { Count: > 0 })
|
||||
{
|
||||
for (int i = 0; i < items.Count; ++i)
|
||||
{
|
||||
EventOfferDiscoPartyReward reward = null;
|
||||
if (i < OfferDiscoPartyRewards.Count)
|
||||
{
|
||||
reward = OfferDiscoPartyRewards[i];
|
||||
}
|
||||
|
||||
if (reward != null)
|
||||
{
|
||||
items[i].Id = reward.ID;
|
||||
items[i].Item = reward.Item;
|
||||
items[i].Number = reward.Number;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTime()
|
||||
{
|
||||
if (OfferDiscoPartyData != null && _fishingEvent != null && OfferDiscoPartyData.EventId == _fishingEvent.ID)
|
||||
{
|
||||
var limitedTime = (LimitedTime)_fishingEvent.TimeDefinition;
|
||||
var startTime = GlobalUtils.TryParseDateTime(limitedTime.StartTime, ZZTimeHelper.UtcNow());
|
||||
var endTime = GlobalUtils.TryParseDateTime(limitedTime.EndTime, ZZTimeHelper.UtcNow().AddDays(-1));
|
||||
OfferDiscoPartyData.StartTime = startTime;
|
||||
OfferDiscoPartyData.EndTime = endTime;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLotteryItemId()
|
||||
{
|
||||
var targetId = CalcLotteryItemId();
|
||||
OfferDiscoPartyData.ItemId = targetId;
|
||||
SaveData();
|
||||
|
||||
ELog($"UpdateLotteryItemId -> {targetId}");
|
||||
}
|
||||
|
||||
public void FinishLottery(int targetId)
|
||||
{
|
||||
ELog($"FinishLottery -> {targetId}");
|
||||
var lastItem = OfferDiscoPartyData.Items.SingleOrDefault(it => it.Id == targetId);
|
||||
if (lastItem is { Status: not TItemStatues.Done })
|
||||
{
|
||||
lastItem.Status = TItemStatues.Done;
|
||||
UpdateForNextLottery();
|
||||
}
|
||||
}
|
||||
//- 开始下一轮数据
|
||||
public void UpdateForNextLottery()
|
||||
{
|
||||
UpdateValidIds();
|
||||
UpdateLotteryItemId();
|
||||
}
|
||||
|
||||
|
||||
public void ClearOfferDiscoPartyData()
|
||||
{
|
||||
OfferDiscoPartyData = null;
|
||||
_isInit = false;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public TOfferDiscoPartyData.TItem GetTItemById(int targetId)
|
||||
{
|
||||
var lastItem = OfferDiscoPartyData.Items.FirstOrDefault(it => it.Id == targetId);
|
||||
return lastItem;
|
||||
}
|
||||
|
||||
public int GetDropIdById(int id)
|
||||
{
|
||||
var item = GetTItemById(id);
|
||||
var itemData = _tables.TbItem.GetOrDefault(item.Item);
|
||||
return itemData.RedirectID;
|
||||
}
|
||||
|
||||
public void OnSettleFinalEvent()
|
||||
{
|
||||
// var item = _tables.TbItem.GetOrDefault(OfferDiscoParty.ChestReward);
|
||||
// var item = _tables.TbItem.GetOrDefault(OfferDiscoPartyData.ChestReward);
|
||||
// var itemFinal = GContext.container.Resolve<PlayerItemData>()
|
||||
// .GetItemDropPackageItemList(item.RedirectID, 1);
|
||||
// GContext.container.Resolve<PlayerItemData>().LureInflation(itemFinal, null);
|
||||
// GContext.container.Resolve<PlayerItemData>().AddItem(itemFinal);
|
||||
// GContext.Publish(new ShowData(itemFinal));
|
||||
// GContext.Publish(new ShowData());
|
||||
// OfferDiscoPartyData.Finished = true;
|
||||
SaveData();
|
||||
GContext.Publish(new OfferDiscoPartyRefreshEvent());
|
||||
}
|
||||
|
||||
public float GetProbability(int id)
|
||||
{
|
||||
var theIdWeight = OfferDiscoPartyRewards.FirstOrDefault(item => item.ID == id)?.Weight ?? 0;
|
||||
var weightTable = OfferDiscoPartyRewards
|
||||
.Where(item => !IsItemFinished(item.ID))
|
||||
.Select(item => item.Weight).ToList();
|
||||
var totalWeight = weightTable.Sum();
|
||||
return (float)Math.Round((float)theIdWeight / totalWeight * 100, 2);
|
||||
}
|
||||
|
||||
private bool IsItemFinished(int itemID)
|
||||
{
|
||||
var lastItem = OfferDiscoPartyData.Items.SingleOrDefault(it => it.Id == itemID);
|
||||
return lastItem?.Status == TItemStatues.Done;
|
||||
}
|
||||
|
||||
public TimeSpan GetTimeRemain()
|
||||
{
|
||||
if (OfferDiscoPartyData != null)
|
||||
{
|
||||
return OfferDiscoPartyData.EndTime - ZZTimeHelper.UtcNow();
|
||||
}
|
||||
|
||||
// 活动未开始
|
||||
return ZZTimeHelper.UtcNow().AddDays(-1) - ZZTimeHelper.UtcNow();
|
||||
}
|
||||
|
||||
public void Settle()
|
||||
{
|
||||
if (IsFinished())
|
||||
{
|
||||
OnSettleFinalEvent();
|
||||
}
|
||||
}
|
||||
|
||||
public bool UIRunning { get; set; } = false;
|
||||
|
||||
// private bool IsCanGetFinalReward()
|
||||
// {
|
||||
// // return OfferDiscoPartyData.LotteryNum >= OfferDiscoPartyData.Items.Count;
|
||||
// // && !OfferDiscoPartyData.Finished;
|
||||
// }
|
||||
|
||||
public void OnBuySuccess()
|
||||
{
|
||||
ELog("OnBuySuccess -> ");
|
||||
OfferDiscoPartyData.RewardList.Add(OfferDiscoPartyData.ItemId);
|
||||
OfferDiscoPartyData.LotteryNum += 1;
|
||||
FinishLottery(OfferDiscoPartyData.ItemId);
|
||||
SaveData();
|
||||
EventTracking();
|
||||
//NEXT TURN
|
||||
if (UIRunning)
|
||||
{
|
||||
// 播放动画逻辑,交给UI
|
||||
return;
|
||||
}
|
||||
|
||||
// FinishLottery(OfferDiscoPartyData.ItemId);
|
||||
UpdateForNextLottery();
|
||||
Settle();
|
||||
}
|
||||
|
||||
public void EventTracking()
|
||||
{
|
||||
var iapItemIdx = OfferDiscoPartyData.LotteryNum - 1 - FreeTime;
|
||||
var price = GetIapItemListThisTime(iapItemIdx)?.PaymentAmount ?? 0F;
|
||||
// var itemEnt = GetTItemById(OfferDiscoPartyData.ItemId);
|
||||
// var rewardStr = string.Join(',', OfferDiscoPartyData.RewardList);
|
||||
var number = OfferDiscoPartyData.LotteryNum;
|
||||
#if AGG
|
||||
using (var e = GEvent.TackEvent("offer_discoparty"))
|
||||
{
|
||||
e.AddContent("event_id", OfferDiscoPartyData.EventId);
|
||||
e.AddContent("number",number);
|
||||
e.AddContent("price", price);
|
||||
e.AddContent("reward",OfferDiscoPartyData.ItemId);
|
||||
}
|
||||
#endif
|
||||
ELog($"EventTracking: event_id: {OfferDiscoPartyData.EventId} number: {number} price: {price} " +
|
||||
$"reward :{OfferDiscoPartyData.ItemId} ");
|
||||
}
|
||||
|
||||
public IAPItemList GetIapItemListThisTime()
|
||||
{
|
||||
return GetIapItemListThisTime(OfferDiscoPartyData.LotteryNum - FreeTime);
|
||||
}
|
||||
|
||||
private IAPItemList GetIapItemListThisTime(int index)
|
||||
{
|
||||
// index -= FreeTime;
|
||||
// index = Mathf.Clamp(index, 0, OfferDiscoParty.IapList.Count - 1);
|
||||
if (index < 0 || index > OfferDiscoParty.IapList.Count - 1)
|
||||
return null;
|
||||
|
||||
var iapId = OfferDiscoParty.IapList[index];
|
||||
var iAPItemList = _tables.TbIAPItemList.GetOrDefault(iapId);
|
||||
return iAPItemList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
using asap.core;
|
||||
using GameCore;
|
||||
using cfg;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using game;
|
||||
using System.Linq;
|
||||
public class PiggyBankPackData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore playerSaveStore { get; set; }
|
||||
private readonly Tables _tables = GContext.container.Resolve<Tables>();
|
||||
private readonly TbEventPackManager _epm
|
||||
= GContext.container.Resolve<Tables>().TbEventPackManager;
|
||||
private struct Data
|
||||
{
|
||||
public int currentEventID;
|
||||
public int purchaseCount;
|
||||
public int VIPLvlWhenEventActivate;
|
||||
public bool doNeedTrigger;
|
||||
public bool isTriggered;
|
||||
public int savingProgress;
|
||||
public int redirectID;
|
||||
public float inflation;
|
||||
public Data(int eventID = 0, int v = 0, bool b = false, int rid = 0, float inflation = 0)
|
||||
{
|
||||
currentEventID = eventID;
|
||||
purchaseCount = 0;
|
||||
VIPLvlWhenEventActivate = v;
|
||||
doNeedTrigger = b;
|
||||
savingProgress = 0;
|
||||
isTriggered = false;
|
||||
redirectID = rid;
|
||||
this.inflation = inflation;
|
||||
}
|
||||
}
|
||||
private Data _data;
|
||||
|
||||
private bool _kvLoadPresent;
|
||||
internal bool KVLoadPresent => _kvLoadPresent;
|
||||
internal static game.PlayerDataStorage.PlayerKVPiggyBankState DecodeForKVLoad(
|
||||
IReadOnlyDictionary<string, string> raw, game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodePiggyBank(raw, snapshot, errors);
|
||||
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
_kvLoadPresent = false;
|
||||
_data = default(Data);
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVPiggyBankState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
if (!state.IsPresent) { ResetForKVLoad(); return; }
|
||||
var replacement = new Data
|
||||
{
|
||||
currentEventID = state.currentEventID,
|
||||
purchaseCount = state.purchaseCount,
|
||||
VIPLvlWhenEventActivate = state.VIPLvlWhenEventActivate,
|
||||
savingProgress = state.savingProgress,
|
||||
redirectID = state.redirectID,
|
||||
doNeedTrigger = state.doNeedTrigger,
|
||||
isTriggered = state.isTriggered,
|
||||
inflation = state.inflation,
|
||||
};
|
||||
_data = replacement;
|
||||
_kvLoadPresent = true;
|
||||
}
|
||||
internal game.PlayerDataStorage.PlayerKVPiggyBankState SnapshotForKVValidation() =>
|
||||
new game.PlayerDataStorage.PlayerKVPiggyBankState(_kvLoadPresent, _data.currentEventID, _data.purchaseCount, _data.VIPLvlWhenEventActivate, _data.savingProgress, _data.redirectID, _data.doNeedTrigger, _data.isTriggered, _data.inflation);
|
||||
public bool IsWithinEventTime
|
||||
{
|
||||
get
|
||||
{
|
||||
var fishingEvent = _tables.TbFishingEvent.GetOrDefault(_data.currentEventID);
|
||||
if (!(fishingEvent?.TimeDefinition is LimitedTime time)) return false;
|
||||
var et = DateTime.Parse(time.EndTime);
|
||||
var st = DateTime.Parse(time.StartTime);
|
||||
return ZZTimeHelper.UtcNow() >= st && ZZTimeHelper.UtcNow() < et;
|
||||
}
|
||||
}
|
||||
public bool IsPackActivated { get => _data.isTriggered && _tables.TbFishingEvent.DataMap.Keys.Contains(_data.currentEventID) && IsWithinEventTime && _data.purchaseCount < _epm[RedirectID].MaxCount; }
|
||||
public bool DoNeedUpdate { get => _data.doNeedTrigger; set => _data.doNeedTrigger = value; }
|
||||
public int CurrentEventID { get => _data.currentEventID; }
|
||||
public int PurchaseCount { get => _data.purchaseCount; }
|
||||
public int VIPLvlWhenEventActivated { get => _data.VIPLvlWhenEventActivate; }
|
||||
public int Progress { get => _data.savingProgress; }
|
||||
public float Inflation { get => _data.inflation; }
|
||||
public bool IsFull { get => _data.savingProgress >= Target; }
|
||||
public int ActivateItemId
|
||||
{
|
||||
get => _tables.TbSpecialPack[_epm[RedirectID]
|
||||
.VIPPackList[_data.VIPLvlWhenEventActivate][0]]
|
||||
.ActivateItemId;
|
||||
}
|
||||
public string ActiveItemImg
|
||||
{
|
||||
get => _tables.TbSpecialPack[_epm[RedirectID]
|
||||
.VIPPackList[_data.VIPLvlWhenEventActivate][0]].ActiveItemImg;
|
||||
}
|
||||
|
||||
public int EventItemId
|
||||
{
|
||||
get
|
||||
{
|
||||
SpecialPack specialPack = GetSpecialPack();
|
||||
if (specialPack != null)
|
||||
{
|
||||
return specialPack.EventItemId;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
SpecialPack GetSpecialPack()
|
||||
{
|
||||
var TbEventPackManager = _tables.TbEventPackManager.GetOrDefault(_data.redirectID);
|
||||
if (TbEventPackManager == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return _tables.TbSpecialPack.GetOrDefault(TbEventPackManager.VIPPackList[_data.VIPLvlWhenEventActivate][0]);
|
||||
}
|
||||
public int Target
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = _tables.TbSpecialPack[_epm[RedirectID]
|
||||
.VIPPackList[_data.VIPLvlWhenEventActivate][0]]
|
||||
.EventItemRequire[0];
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
count = playerItemData.IntInflation(count, _data.inflation);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
public int CanBuyCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = _tables.TbSpecialPack[_epm[RedirectID]
|
||||
.VIPPackList[_data.VIPLvlWhenEventActivate][0]]
|
||||
.CanBuyCount;
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
count = playerItemData.IntInflation(count, _data.inflation);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
//public int RedirectID { get => _tables.TbFishingEvent[_data.currentEventID].RedirectID; }
|
||||
public int RedirectID { get => _data.redirectID; }
|
||||
public int RewardDropID
|
||||
{
|
||||
get => _tables.TbSpecialPack[_epm[RedirectID]
|
||||
.VIPPackList[_data.VIPLvlWhenEventActivate][0]]
|
||||
.DropID[0];
|
||||
}
|
||||
public List<int> FishingScoreList
|
||||
{
|
||||
get => _tables.TbSpecialPack[_epm[RedirectID].VIPPackList[_data.VIPLvlWhenEventActivate][0]]
|
||||
.EventItemGet;
|
||||
}
|
||||
public TimeSpan RemainingTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return DateTime.Parse((_tables.TbFishingEvent[_data.currentEventID].TimeDefinition as LimitedTime).EndTime)
|
||||
- ZZTimeHelper.UtcNow();
|
||||
}
|
||||
}
|
||||
public int MaxCount { get => _epm[RedirectID].MaxCount; }
|
||||
public void LoadData(string s)
|
||||
{
|
||||
_data = Newtonsoft.Json.JsonConvert.DeserializeObject<Data>(s);
|
||||
|
||||
//_data.redirectID = 0;
|
||||
if (_data.redirectID == 0)
|
||||
{
|
||||
_data.redirectID = 4055001;
|
||||
}
|
||||
}
|
||||
public void UpdateData(cfg.FishingEvent e)
|
||||
{
|
||||
if (_epm[_tables.TbFishingEvent[e.ID].RedirectID].PackType != 2)
|
||||
{
|
||||
Debug.LogError($"Fishing event ID {e.ID} is not a piggyBank event.");
|
||||
return;
|
||||
}
|
||||
if (_data.currentEventID != e.ID)
|
||||
_data = new Data(e.ID, GContext.container.Resolve<PlayerData>().PriceLv, true, e.RedirectID, GContext.container.Resolve<PlayerData>().InflationRate);
|
||||
if (_data.redirectID != e.RedirectID)
|
||||
{
|
||||
_data.redirectID = e.RedirectID;
|
||||
Debug.Log("working!!!!!!!!!!");
|
||||
}
|
||||
SaveData();
|
||||
}
|
||||
public void SaveData()
|
||||
{
|
||||
playerSaveStore.Write("PiggyBankPackData",
|
||||
Newtonsoft.Json.JsonConvert.SerializeObject(_data)).RequireAccepted();
|
||||
}
|
||||
public bool AddProgress(int fishTier, int magnification)
|
||||
{
|
||||
if (!IsPackActivated || fishTier < 1 || fishTier > 5)
|
||||
{
|
||||
//Debug.LogError($"Fish Tier {fishTier} not defined in TbSpedialPack");
|
||||
return false;
|
||||
}
|
||||
var TbSpecialPack = _tables.TbSpecialPack[_epm[RedirectID].VIPPackList[_data.VIPLvlWhenEventActivate][0]];
|
||||
int progress = TbSpecialPack.EventItemGet[fishTier - 1] * magnification;
|
||||
if (progress > 0 && _data.savingProgress < Target)
|
||||
{
|
||||
int curProgress = _data.savingProgress;
|
||||
_data.savingProgress += progress;
|
||||
GContext.Publish(new TargetAddData(TbSpecialPack.EventItemId, curProgress, progress));
|
||||
if (_data.savingProgress >= Target)
|
||||
{
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUIForce(_data.currentEventID, UITypes.GiftPiggyBankPopupPanel);
|
||||
_data.savingProgress = Target;
|
||||
SaveData();
|
||||
return true;
|
||||
}
|
||||
SaveData();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public void AddPurchase()
|
||||
{
|
||||
_data.purchaseCount++;
|
||||
SaveData();
|
||||
}
|
||||
public void TriggerPack()
|
||||
{
|
||||
DoNeedUpdate = false;
|
||||
_data.isTriggered = true;
|
||||
int EventItemCountFirst = _tables.TbSpecialPack[_epm[RedirectID]
|
||||
.VIPPackList[_data.VIPLvlWhenEventActivate][0]].EventItemCountFirst;
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
EventItemCountFirst = playerItemData.IntInflation(EventItemCountFirst, _data.inflation);
|
||||
_data.savingProgress += EventItemCountFirst;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
public class PiggyBankProgressEvent
|
||||
{
|
||||
public int type;//0: 初始化 1:增加动画
|
||||
public int addProgress;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,681 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public class RodGSAData
|
||||
{
|
||||
public int GloveID;
|
||||
public Dictionary<int, int> RodToSkin = new Dictionary<int, int>();
|
||||
public List<int> GloveIds = new List<int>();
|
||||
public List<int> SkinIds = new List<int>();
|
||||
|
||||
public List<int> GloveNewIds = new List<int>();
|
||||
public List<int> SkinNewIds = new List<int>();
|
||||
}
|
||||
public partial class PlayerFishData
|
||||
{
|
||||
#region 鱼杆皮肤
|
||||
public RodGSAData rodAccessoriesData;
|
||||
string InitRodAccessoriesDataKey = "InitRodAccessoriesData";
|
||||
public GloveData GetGloveData
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tables.TbGloveData.GetOrDefault(rodAccessoriesData.GloveID);
|
||||
}
|
||||
}
|
||||
public void InitRodAccessoriesData()
|
||||
{
|
||||
if (rodAccessoriesData == null || rodAccessoriesData.GloveID == 0)
|
||||
{
|
||||
rodAccessoriesData = new RodGSAData();
|
||||
rodAccessoriesData.GloveID = _tables.TbGlobalConfig.InitGloveID;
|
||||
rodAccessoriesData.GloveIds = new List<int>() { _tables.TbGlobalConfig.InitGloveID };
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取鱼杆对应的皮肤
|
||||
/// </summary>
|
||||
/// <param name="rodId"></param>
|
||||
/// <returns></returns>
|
||||
public int GetRodSkin(int rodId)
|
||||
{
|
||||
if (rodAccessoriesData.RodToSkin.ContainsKey(rodId))
|
||||
{
|
||||
return rodAccessoriesData.RodToSkin[rodId];
|
||||
}
|
||||
return rodId;
|
||||
}
|
||||
/// <summary>
|
||||
/// 设置鱼杆对应的皮肤
|
||||
/// </summary>
|
||||
/// <param name="rodId"></param>
|
||||
/// <param name="skinId"></param>
|
||||
public void SetRodSkin(int rodId, int skinId)
|
||||
{
|
||||
if (skinId <= 0)
|
||||
{
|
||||
skinId = rodId;
|
||||
}
|
||||
rodAccessoriesData.RodToSkin[rodId] = skinId;
|
||||
SaveInitRodAccessoriesData();
|
||||
}
|
||||
public void InitRodAccessoriesData(string value)
|
||||
{
|
||||
rodAccessoriesData = Newtonsoft.Json.JsonConvert.DeserializeObject<RodGSAData>(value);
|
||||
}
|
||||
public void SaveInitRodAccessoriesData()
|
||||
{
|
||||
string value = Newtonsoft.Json.JsonConvert.SerializeObject(rodAccessoriesData);
|
||||
playerSaveStore.Write(InitRodAccessoriesDataKey, value).RequireAccepted();
|
||||
}
|
||||
/// <summary>
|
||||
/// 手是否解锁
|
||||
/// </summary>
|
||||
/// <param name="gloveId"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsGloveUnlocked(int gloveId)
|
||||
{
|
||||
return rodAccessoriesData.GloveIds.Contains(gloveId);
|
||||
}
|
||||
/// <summary>
|
||||
/// 皮肤是否解锁
|
||||
/// </summary>
|
||||
/// <param name="skinId"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsSkinUnlocked(int skinId)
|
||||
{
|
||||
return rodAccessoriesData.SkinIds.Contains(skinId);
|
||||
}
|
||||
/// <summary>
|
||||
/// 解锁手
|
||||
/// </summary>
|
||||
/// <param name="gloveId"></param>
|
||||
public void UnlockGlove(int gloveId)
|
||||
{
|
||||
if (!IsGloveUnlocked(gloveId))
|
||||
{
|
||||
rodAccessoriesData.GloveIds.Add(gloveId);
|
||||
rodAccessoriesData.GloveNewIds.Add(gloveId);
|
||||
SaveInitRodAccessoriesData();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("手投放重复:" + gloveId);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 解锁皮肤
|
||||
/// </summary>
|
||||
/// <param name="skinId"></param>
|
||||
public void UnlockSkin(int skinId)
|
||||
{
|
||||
if (!IsSkinUnlocked(skinId))
|
||||
{
|
||||
rodAccessoriesData.SkinIds.Add(skinId);
|
||||
rodAccessoriesData.SkinNewIds.Add(skinId);
|
||||
SaveInitRodAccessoriesData();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("鱼杆皮肤投放重复:" + skinId);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取是否是没查看过的手
|
||||
/// </summary>
|
||||
/// <param name="skinId"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsNewGlove(int skinId)
|
||||
{
|
||||
return rodAccessoriesData.GloveNewIds.Contains(skinId);
|
||||
}
|
||||
/// <summary>
|
||||
/// 是否是没查看过的皮肤
|
||||
/// </summary>
|
||||
/// <param name="skinId"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsNewSkin(int skinId)
|
||||
{
|
||||
bool isNew = rodAccessoriesData.SkinNewIds.Contains(skinId);
|
||||
if (isNew)
|
||||
{
|
||||
rodAccessoriesData.SkinNewIds.Remove(skinId);
|
||||
}
|
||||
return isNew;
|
||||
}
|
||||
/// <summary>
|
||||
/// 取消new标记 手
|
||||
/// </summary>
|
||||
/// <param name="gloveId"></param>
|
||||
public void RemoveGloveNewId()
|
||||
{
|
||||
if (rodAccessoriesData.GloveNewIds.Count > 0)
|
||||
{
|
||||
rodAccessoriesData.GloveNewIds.Clear();
|
||||
SaveInitRodAccessoriesData();
|
||||
}
|
||||
}
|
||||
#endregion 鱼杆皮肤
|
||||
#region 鱼杆升级
|
||||
//合成或强化鱼竿
|
||||
bool EnhanceConsumed(int quality, int fragmentID, int rquiredFragments)
|
||||
{
|
||||
if (rodData.TryGetValue(fragmentID, out int rodCount) && rodCount >= rquiredFragments)
|
||||
{
|
||||
rodData[fragmentID] -= rquiredFragments;
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("item_change", gaSend: true))
|
||||
{
|
||||
e.AddContent("item_id", fragmentID)
|
||||
.AddContent("state_info", "cost")
|
||||
.AddContent("change_num", rquiredFragments);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
rquiredFragments -= rodCount;
|
||||
RodFragmentExchange rodFragmentExchange = _tables.TbRodFragmentExchange.GetOrDefault(quality);
|
||||
int fragment = 0;
|
||||
if (rodFragmentExchange != null)
|
||||
{
|
||||
rquiredFragments *= rodFragmentExchange.ExchangeRate;
|
||||
fragment = GContext.container.Resolve<PlayerFishData>().GetRodPiece(rodFragmentExchange.GeneralFragmentID);
|
||||
}
|
||||
if (fragment >= rquiredFragments)
|
||||
{
|
||||
if (rodData.ContainsKey(fragmentID))
|
||||
{
|
||||
rodData[fragmentID] = 0;
|
||||
}
|
||||
rodData[rodFragmentExchange.GeneralFragmentID] -= rquiredFragments;
|
||||
#if AGG
|
||||
|
||||
if (rodCount > 0)
|
||||
{
|
||||
using (var e = GEvent.GameEvent("item_change", gaSend: true))
|
||||
{
|
||||
e.AddContent("item_id", fragmentID)
|
||||
.AddContent("state_info", "cost")
|
||||
.AddContent("change_num", rodCount);
|
||||
}
|
||||
}
|
||||
using (var e = GEvent.GameEvent("item_change", gaSend: true))
|
||||
{
|
||||
e.AddContent("item_id", rodFragmentExchange.GeneralFragmentID)
|
||||
.AddContent("state_info", "cost")
|
||||
.AddContent("change_num", rquiredFragments);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public void AscendRod(int id)
|
||||
{
|
||||
RodData _rodData = _tables.TbRodData.GetOrDefault(id);
|
||||
if (_rodData != null)
|
||||
{
|
||||
bool isUp = false;
|
||||
RodAscend rodAscend = _tables.TbRodAscend.GetOrDefault(_rodData.AscendID);
|
||||
int rquiredFragments;
|
||||
int fragmentID = rodAscend.FragmentID;
|
||||
int quality = _rodData.Quality;
|
||||
if (rodData.ContainsKey(id))
|
||||
{
|
||||
rquiredFragments = rodAscend.AscentTransformed[rodData[id] + 1];
|
||||
|
||||
if (rodData[id] < rodAscend.AscentTransformed.Count - 1 && EnhanceConsumed(quality, fragmentID, rquiredFragments))
|
||||
{
|
||||
isUp = true;
|
||||
rodData[id] += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rquiredFragments = rodAscend.AscentTransformed[0];
|
||||
|
||||
if (rodData.TryGetValue(fragmentID, out int rodCount) && rodCount >= rquiredFragments)
|
||||
{
|
||||
rodData[fragmentID] -= rquiredFragments;
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("item_change", gaSend: true))
|
||||
{
|
||||
e.AddContent("item_id", fragmentID)
|
||||
.AddContent("state_info", "cost")
|
||||
.AddContent("change_num", rquiredFragments);
|
||||
}
|
||||
#endif
|
||||
isUp = true;
|
||||
rodData.Add(id, 0);
|
||||
}
|
||||
}
|
||||
if (isUp)
|
||||
{
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("rod_upgrade"))
|
||||
{
|
||||
e.AddContent("rod_id", id)
|
||||
.AddContent("rod_level", GetRodLevel(id))
|
||||
.AddContent("resin_consume", 0)
|
||||
.AddContent("rod_star", rodData[id])
|
||||
.AddContent("rod_chips_consume", rquiredFragments);
|
||||
}
|
||||
#endif
|
||||
playerSaveStore.Write("RodData", JsonMapper.ToJson(rodData)).RequireAccepted();
|
||||
//鱼竿红点数据修改
|
||||
CheckRodRedPointUp();
|
||||
//合成鱼竿
|
||||
CheckAllRodUpLevel();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region 荣耀等级
|
||||
|
||||
Dictionary<int, int> _honorlevel = new Dictionary<int, int>();
|
||||
public int GetHonorLevel(int honorId)
|
||||
{
|
||||
if (_honorlevel.TryGetValue(honorId, out int level))
|
||||
{
|
||||
return level;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public int GetHonorLevelAttribute(int quality, int starAdd = 0)
|
||||
{
|
||||
RodRodLevelPeak rodLevelPeak = _tables.TbRodRodLevelPeak.GetOrDefault(quality);
|
||||
//星级对属性的加成 某几个属性加多少加到多少星
|
||||
int level = GetHonorLevel(quality) + starAdd;
|
||||
if (rodLevelPeak != null && level > 0)
|
||||
{
|
||||
List<int> peakBasicStats = rodLevelPeak.PeakBasicStats;
|
||||
int value;
|
||||
int count = peakBasicStats.Count;
|
||||
if (level > count)
|
||||
{
|
||||
value = peakBasicStats[count - 1];
|
||||
value += (level - count) * (peakBasicStats[count - 1] - peakBasicStats[count - 2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = peakBasicStats[level - 1];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public void HonorLevelUp(RodData rodData)
|
||||
{
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
RodAscend rodAscend = _tables.TbRodAscend.GetOrDefault(rodData.AscendID);
|
||||
int quality = rodData.Quality;
|
||||
int fragmentID = rodAscend.FragmentID;
|
||||
RodRodLevelPeak rodLevelPeak = _tables.TbRodRodLevelPeak.GetOrDefault(rodData.Quality);
|
||||
if (rodLevelPeak == null)
|
||||
{
|
||||
Debug.LogError($"[FishingRodHonorlevelPanel]OnClickUpGray: No RodRodLevelPeak data found for Quality {quality}");
|
||||
return;
|
||||
}
|
||||
int currentHonorLevel = GetHonorLevel(rodData.Quality);
|
||||
if (currentHonorLevel >= rodLevelPeak.ItemConsume.Count)
|
||||
{
|
||||
currentHonorLevel = rodLevelPeak.ItemConsume.Count - 1;
|
||||
}
|
||||
int requiredItems = rodLevelPeak.ItemConsume[currentHonorLevel];
|
||||
int fragmentItems = rodLevelPeak.FragmentConsume[currentHonorLevel];
|
||||
if (playerData.pearl >= requiredItems && EnhanceConsumed(quality, fragmentID, fragmentItems))
|
||||
{
|
||||
playerData.SetPearl(playerData.pearl - requiredItems);
|
||||
if (_honorlevel.ContainsKey(quality))
|
||||
{
|
||||
_honorlevel[quality]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_honorlevel[quality] = 1;
|
||||
}
|
||||
SaveHonorLevelData();
|
||||
}
|
||||
}
|
||||
|
||||
string HonorLevelDataKey = "HonorLevelData";
|
||||
|
||||
public void SaveHonorLevelData()
|
||||
{
|
||||
string value = Newtonsoft.Json.JsonConvert.SerializeObject(_honorlevel);
|
||||
playerSaveStore.Write(HonorLevelDataKey, value).RequireAccepted();
|
||||
}
|
||||
|
||||
#endregion 荣耀等级
|
||||
|
||||
#region 鱼杆词条星级相关
|
||||
|
||||
//计算升级和强化后的属性
|
||||
public List<int> GetRodAttribute(int id, bool isMax = false, int levelAdd = 0, int starAdd = 0)
|
||||
{
|
||||
RodData rodData = _tables.TbRodData.GetOrDefault(id);
|
||||
RodAscend rodAscend = _tables.TbRodAscend.GetOrDefault(rodData.AscendID);
|
||||
RodLevelup rodLevelup = _tables.TbRodLevelup.GetOrDefault(rodData.LevelupID);
|
||||
int star = isMax ? rodAscend.MaxAscent : GetRodPiece(rodData.ID) + starAdd;
|
||||
int level = isMax ? rodLevelup.MaxLevel - 1 : GetRodLevel(rodData.ID) + levelAdd;
|
||||
List<int> list = new List<int>();
|
||||
for (int i = 0; i < rodData.InitialBasicStats.Count; i++)
|
||||
{
|
||||
list.Add(0);
|
||||
}
|
||||
//星级对属性的加成 某几个属性加多少加到多少星
|
||||
var AscentBasicStats = rodAscend.AscentBasicStat;
|
||||
var AscentBasicStatList = rodAscend.AscentBasicStatList;
|
||||
int index;
|
||||
for (int i = 0; i < star; i++)
|
||||
{
|
||||
var AscentBasicStat = AscentBasicStats[i];
|
||||
int count = AscentBasicStat.Count;
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
//InitialBasicStats 中第 AscentBasicStat[j] 个值加 i 星的值 累加
|
||||
index = AscentBasicStat[j] - 1;
|
||||
list[index] = AscentBasicStatList[i][j];
|
||||
}
|
||||
}
|
||||
//等级对属性的加成 每个属性升到多少级
|
||||
var levelupBasicStats = rodLevelup.LevelupBasicStats;
|
||||
for (int j = 0; j < list.Count; j++)
|
||||
{
|
||||
list[j] += levelupBasicStats[j][level] + rodData.InitialBasicStats[j];
|
||||
}
|
||||
list[0] += GetHonorLevelAttribute(rodData.Quality);
|
||||
return list;
|
||||
}
|
||||
#region 工具
|
||||
//获取鱼竿某一星级各个词条的等级
|
||||
public int[] GetPerkLevel(int perkIDListCount, int star, List<int> DefaultPerk, List<int> PerkUnlockOrder)
|
||||
{
|
||||
int[] lv = new int[perkIDListCount];
|
||||
for (int i = 0; i < DefaultPerk.Count; i++)
|
||||
{
|
||||
lv[DefaultPerk[i] - 1]++;
|
||||
}
|
||||
for (int i = 0; i < star; i++)
|
||||
{
|
||||
if (PerkUnlockOrder[i] <= perkIDListCount)
|
||||
{
|
||||
//每一星升级的是第几个特性
|
||||
lv[PerkUnlockOrder[i] - 1]++;
|
||||
}
|
||||
}
|
||||
return lv;
|
||||
}
|
||||
//获取鱼竿某一星级能解锁的词条ID及值
|
||||
public Dictionary<int, string> GetRodDuelPerk(int id, int star)
|
||||
{
|
||||
RodData rodData = _tables.TbRodData.GetOrDefault(id);
|
||||
RodAscend curRodAscend = _tables.TbRodAscend.GetOrDefault(rodData.AscendID);
|
||||
int perkIDListCount = curRodAscend.PerkIDList.Count;
|
||||
int[] lv = GetPerkLevel(perkIDListCount, star, curRodAscend.DefaultPerk, curRodAscend.PerkUnlockOrder);
|
||||
Dictionary<int, string> RodPerkDic = new Dictionary<int, string>();
|
||||
for (int i = 0; i < perkIDListCount; i++)
|
||||
{
|
||||
int level = lv[i];
|
||||
if (level > 0)
|
||||
{
|
||||
var rodEngancePerk = _tables.TbRodAscendPerk.GetOrDefault(curRodAscend.PerkIDList[i]);
|
||||
|
||||
if (rodEngancePerk.DuelPerk)
|
||||
{
|
||||
List<string> PerkDataList = curRodAscend.PerkDataList[i];
|
||||
if (level > PerkDataList.Count)
|
||||
{
|
||||
Debug.LogError($"GetRodPerk: rod {id} perkID {curRodAscend.PerkIDList[i]} level {level} is out of range (1 - {PerkDataList.Count})");
|
||||
}
|
||||
else
|
||||
{
|
||||
RodPerkDic[curRodAscend.PerkIDList[i]] = PerkDataList[level - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return RodPerkDic;
|
||||
}
|
||||
//获取鱼竿某一星级能解锁的词条类型及值
|
||||
public Dictionary<RodPerkType, string> GetRodPerk(int id, int star)
|
||||
{
|
||||
RodData rodData = _tables.TbRodData.GetOrDefault(id);
|
||||
RodAscend curRodAscend = _tables.TbRodAscend.GetOrDefault(rodData.AscendID);
|
||||
int perkIDListCount = curRodAscend.PerkIDList.Count;
|
||||
int[] lv = GetPerkLevel(perkIDListCount, star, curRodAscend.DefaultPerk, curRodAscend.PerkUnlockOrder);
|
||||
Dictionary<RodPerkType, string> RodPerkDic = new Dictionary<RodPerkType, string>();
|
||||
for (int i = 0; i < curRodAscend.PerkDataList.Count; i++)
|
||||
{
|
||||
int level = lv[i];
|
||||
if (level > 0)
|
||||
{
|
||||
var rodEngancePerk = _tables.TbRodAscendPerk.GetOrDefault(curRodAscend.PerkIDList[i]);
|
||||
//第i条第lv[i]级 直接读数值
|
||||
List<string> PerkDataList = curRodAscend.PerkDataList[i];
|
||||
if (level > PerkDataList.Count)
|
||||
{
|
||||
Debug.LogError($"GetRodPerk: rod {id} perkID {curRodAscend.PerkIDList[i]} level {level} is out of range (1 - {PerkDataList.Count})");
|
||||
}
|
||||
else
|
||||
{
|
||||
RodPerkDic[rodEngancePerk.PerkType] = PerkDataList[level - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return RodPerkDic;
|
||||
}
|
||||
//获取鱼竿某一星级能解锁的词条类型及等级
|
||||
public Dictionary<RodPerkType, (int, int)> GetRodPerkLevel(int id, int star)
|
||||
{
|
||||
RodData rodData = _tables.TbRodData.GetOrDefault(id);
|
||||
RodAscend curRodAscend = _tables.TbRodAscend.GetOrDefault(rodData.AscendID);
|
||||
int perkIDListCount = curRodAscend.PerkIDList.Count;
|
||||
int[] lv = GetPerkLevel(perkIDListCount, star, curRodAscend.DefaultPerk, curRodAscend.PerkUnlockOrder);
|
||||
Dictionary<RodPerkType, (int, int)> RodPerkDic = new Dictionary<RodPerkType, (int, int)>();
|
||||
for (int i = 0; i < perkIDListCount; i++)
|
||||
{
|
||||
int level = lv[i];
|
||||
if (level > 0)
|
||||
{
|
||||
var rodEngancePerk = _tables.TbRodAscendPerk.GetOrDefault(curRodAscend.PerkIDList[i]);
|
||||
RodPerkDic[rodEngancePerk.PerkType] = (curRodAscend.PerkIDList[i], level);
|
||||
}
|
||||
}
|
||||
return RodPerkDic;
|
||||
}
|
||||
|
||||
|
||||
//解析词条的值
|
||||
public float GetRodPerkValue(RodPerkType rodPerkType, Dictionary<RodPerkType, string> RodPerkDic)
|
||||
{
|
||||
if (RodPerkDic.TryGetValue(rodPerkType, out string perk))
|
||||
{
|
||||
if (perk.Contains('|'))
|
||||
{
|
||||
Debug.LogError($"GetRodPerkValue: rodPerkType {rodPerkType} perk {perk} cannot convert to float directly");
|
||||
string[] values = perk.Split('|');
|
||||
return float.Parse(values[0]);
|
||||
}
|
||||
return float.Parse(perk);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static string GetPerkDesc(RodPerkType rodPerkType, string des, string value)
|
||||
{
|
||||
var values = value.Split('|');
|
||||
string str = LocalizationMgr.GetText(des);
|
||||
try
|
||||
{
|
||||
switch (rodPerkType)
|
||||
{
|
||||
case RodPerkType.BattlePerk_AllToAtkSpeed:
|
||||
str = LocalizationMgr.GetFormatTextValue(des, float.Parse(values[0]).ToPercentageString(), values[1], float.Parse(values[2]).ToPercentageString());
|
||||
break;
|
||||
case RodPerkType.BattlePerk_ComboToExDmg:
|
||||
str = LocalizationMgr.GetFormatTextValue(des, float.Parse(values[0]).ToPercentageString(), values[1]);
|
||||
break;
|
||||
case RodPerkType.BattlePerk_FishSkillToAtkSpeed:
|
||||
case RodPerkType.BattlePerk_FishEscapeToAtkSpeed:
|
||||
str = LocalizationMgr.GetFormatTextValue(des, values[0], float.Parse(values[1]).ToPercentageString());
|
||||
break;
|
||||
case RodPerkType.BattlePerk_PiercingAddDamage:
|
||||
str = LocalizationMgr.GetFormatTextValue(des, values[1], float.Parse(values[0]).ToPercentageString());
|
||||
break;
|
||||
case RodPerkType.BattlePerk_PiercingToCombo:
|
||||
case RodPerkType.BattlePerk_TensionToCrit:
|
||||
case RodPerkType.BattlePerk_PiercingCritDmg:
|
||||
|
||||
case RodPerkType.FactoryLabel_ScaleG:
|
||||
case RodPerkType.FactoryLabel_Ilmarinen:
|
||||
case RodPerkType.FactoryLabel_ChromaCast:
|
||||
case RodPerkType.FactoryLabel_Victoria:
|
||||
case RodPerkType.FactoryLabel_Sakura:
|
||||
case RodPerkType.FactoryLabel_Titan:
|
||||
str = LocalizationMgr.GetFormatTextValue(des, float.Parse(values[0]).ToPercentageString(), float.Parse(values[1]).ToPercentageString());
|
||||
break;
|
||||
case RodPerkType.BattlePerk_ComboToCombo:
|
||||
str = LocalizationMgr.GetFormatTextValue(des, float.Parse(values[0]).ToPercentageString(), float.Parse(values[1]).ToPercentageString(), float.Parse(values[2]).ToPercentageString());
|
||||
break;
|
||||
case RodPerkType.NewPiercingZone:
|
||||
break;
|
||||
default:
|
||||
if (!value.Contains('|'))
|
||||
{
|
||||
str = LocalizationMgr.GetFormatTextValue(des, float.Parse(value).ToPercentageString());
|
||||
}
|
||||
else
|
||||
{
|
||||
str = LocalizationMgr.GetFormatTextValue(des, float.Parse(values[0]).ToPercentageString(), float.Parse(values[1]).ToPercentageString());
|
||||
Debug.LogError($"GetPerkDesc error: no case for rodPerkType: {rodPerkType}, des: {des}, value: {value}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"GetPerkDesc error: {e.Message}, rodPerkType: {rodPerkType}, des: {des}, value: {value}");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
/// <summary>
|
||||
/// 设置星级展示
|
||||
/// </summary>
|
||||
/// <param name="star"></param>
|
||||
/// <param name="MaxAscent"></param>
|
||||
/// <param name="star_empty_list"></param>
|
||||
/// <param name="star_empty_image"></param>
|
||||
/// <param name="starList"></param>
|
||||
/// <param name="isUp"></param>
|
||||
/// <returns></returns>
|
||||
public static int SetRodStar(int star,
|
||||
int MaxAscent,
|
||||
List<GameObject> star_empty_list,
|
||||
List<Image> star_empty_image,
|
||||
List<StarItem> starList, bool isUp = false)
|
||||
{
|
||||
if (star > MaxAscent || star < 0)
|
||||
{
|
||||
Debug.LogError($"SetRodStar: star {star} is out of range (0 - {MaxAscent})");
|
||||
star = MaxAscent;
|
||||
}
|
||||
int Quality = MaxAscent / 3;
|
||||
if (MaxAscent > 15)
|
||||
{
|
||||
Debug.LogError("MaxAscent >> show == 15");
|
||||
Quality = MaxAscent / 4;
|
||||
}
|
||||
if (Quality > 5)
|
||||
{
|
||||
Debug.LogError("MaxAscent >> show == 20");
|
||||
Quality = 5;
|
||||
}
|
||||
|
||||
int show = star;
|
||||
int startIndex = 0;
|
||||
if (star > Quality * 3)
|
||||
{
|
||||
show = star - Quality * 3;
|
||||
startIndex = 3;
|
||||
}
|
||||
else if (star > Quality * 2)
|
||||
{
|
||||
show = star - Quality * 2;
|
||||
startIndex = 2;
|
||||
}
|
||||
else if (star > Quality)
|
||||
{
|
||||
show = star - Quality;
|
||||
startIndex = 1;
|
||||
}
|
||||
|
||||
int index = startIndex;
|
||||
for (int i = 0; i < starList.Count; i++)
|
||||
{
|
||||
starList[i].gameObject.SetActive(i < Quality);
|
||||
star_empty_list[i].SetActive(i < Quality);
|
||||
if (i < show)
|
||||
{
|
||||
starList[i].SetStar(startIndex + 1);
|
||||
}
|
||||
else if (i < Quality)
|
||||
{
|
||||
starList[i].SetStar(startIndex);
|
||||
}
|
||||
if (star_empty_image != null)
|
||||
{
|
||||
if (index > 0)
|
||||
{
|
||||
if (index > 3)
|
||||
{
|
||||
Debug.LogError("SetRodStar index > 3, index = " + index);
|
||||
index = 3;
|
||||
}
|
||||
star_empty_image[i].gameObject.SetActive(true);
|
||||
star_empty_image[i].sprite = starList[i].Star[index - 1].sprite;
|
||||
}
|
||||
else
|
||||
{
|
||||
star_empty_image[i].gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isUp)
|
||||
{
|
||||
if (show > 0)
|
||||
{
|
||||
starList[show - 1].PlayAni();
|
||||
if (star_empty_image != null)
|
||||
{
|
||||
index = startIndex;
|
||||
if (index > 0)
|
||||
{
|
||||
star_empty_image[show - 1].gameObject.SetActive(true);
|
||||
star_empty_image[show - 1].sprite = starList[show - 1].Star[index - 1].sprite;
|
||||
}
|
||||
else
|
||||
{
|
||||
star_empty_image[show - 1].gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Quality;
|
||||
}
|
||||
#endregion 工具
|
||||
#endregion 鱼杆词条星级相关
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
namespace GameCore
|
||||
{
|
||||
public partial class PlayerItemData
|
||||
{
|
||||
/// <summary>
|
||||
/// 获得膨胀体力后的物品显示
|
||||
/// </summary>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <param name="dropId"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public ItemData GetItemDataOneLureInflation(float? inflate, int dropId, int index = 0)
|
||||
{
|
||||
DropPackageList dropPackageList = _tables.TbDrop.GetOrDefault(dropId)?.DropList;
|
||||
if (dropPackageList == null)
|
||||
{
|
||||
GameDebug.LogError("null dropID" + dropId);
|
||||
return null;
|
||||
}
|
||||
int len = dropPackageList.DropIDList.Count;
|
||||
int len2 = dropPackageList.DropCountList.Count;
|
||||
if (len != len2)
|
||||
{
|
||||
if (len > len2)
|
||||
{
|
||||
len = len2;
|
||||
}
|
||||
Debug.LogError($"[PlayerItemData]GetItemDataByDropIdAndType: DropPackageList count not equal [DropId]=={dropId}");
|
||||
}
|
||||
if (index >= len)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ItemData itemData = new ItemData()
|
||||
{
|
||||
id = dropPackageList.DropIDList[index],
|
||||
count = dropPackageList.DropCountList[index]
|
||||
};
|
||||
|
||||
ItemTransition(itemData);
|
||||
if (inflate == null)
|
||||
{
|
||||
inflate = GContext.container.Resolve<PlayerData>().InflationRate;//等级膨胀系数
|
||||
}
|
||||
itemData.inflate = (float)inflate;
|
||||
if (itemData.id == 1001)
|
||||
{
|
||||
ItemInflation(itemData, inflate);
|
||||
}
|
||||
return itemData;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获得膨胀体力后的物品显示
|
||||
/// </summary>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <returns></returns>
|
||||
public List<ItemData> GetItemDataByDropIdAndTypeLureInflation(float? inflate, int dropId, int mapId = -1)
|
||||
{
|
||||
List<ItemData> itemDataList = GetItemDataByDropIdAndType(dropId, mapId);
|
||||
LureInflation(itemDataList, inflate);
|
||||
return itemDataList;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获得膨胀体力后的物品显示 处理有选定地图鱼卡的情况
|
||||
/// </summary>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <returns></returns>
|
||||
public List<ItemData> GetItemDataByDropIdLureInflation(float? inflate, int dropId, int mapId = -1, bool afterAdding = false)
|
||||
{
|
||||
List<ItemData> itemDataList = GetItemDataByDropId(dropId, mapId, afterAdding);
|
||||
LureInflation(itemDataList, inflate);
|
||||
return itemDataList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获得膨胀体力后的物品显示
|
||||
/// </summary>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <param name="dropIDList"></param>
|
||||
/// <param name="isShow"></param>
|
||||
/// <returns></returns>
|
||||
public List<ItemData> GetItemByDropListLureInflation(float? inflate, List<int> dropIDList)
|
||||
{
|
||||
if (dropIDList == null || dropIDList.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
List<ItemData> itemDataList = new List<ItemData>();
|
||||
foreach (var t in dropIDList)
|
||||
{
|
||||
List<ItemData> itemDatas = GetItemDataByDropIdAndType(t);
|
||||
if (itemDatas != null && itemDatas.Count > 0)
|
||||
{
|
||||
itemDataList.AddRange(itemDatas);
|
||||
}
|
||||
}
|
||||
//itemDataList不需要重复合并
|
||||
if (itemDataList.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
LureInflation(itemDataList, inflate);
|
||||
return itemDataList;
|
||||
}
|
||||
/// <summary>
|
||||
/// 发放物品 并且膨胀体力
|
||||
/// </summary>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <param name="dropID"></param>
|
||||
/// <param name="isShow"></param>
|
||||
/// <param name="rewardType"></param>
|
||||
/// <returns></returns>
|
||||
public List<ItemData> AddItemByDropLureInflation(float? inflate, int dropID, ChangeSource changeSource, bool isShow = true, RewardType rewardType = RewardType.Normal)
|
||||
{
|
||||
List<ItemData> itemDataList = GetItemDataByDropIdAndType(dropID);
|
||||
if (itemDataList != null)
|
||||
{
|
||||
LureInflation(itemDataList, inflate);
|
||||
if (isShow)
|
||||
{
|
||||
game.PlayerDataStorage.PlayerSaveAction.Present(() => GContext.Publish(new ShowData(itemDataList, rewardType)));
|
||||
}
|
||||
AddItem(itemDataList, changeSource);
|
||||
}
|
||||
return itemDataList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发放物品 并且膨胀体力
|
||||
/// </summary>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <param name="dropIDList"></param>
|
||||
/// <param name="isShow"></param>
|
||||
/// <returns></returns>
|
||||
public List<ItemData> AddItemByDropListLureInflation(float? inflate, List<int> dropIDList, ChangeSource changeSource, bool isShow = true, RewardType rewardType = RewardType.Normal)
|
||||
{
|
||||
if (dropIDList == null || dropIDList.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
List<ItemData> itemDataList = new List<ItemData>();
|
||||
foreach (var t in dropIDList)
|
||||
{
|
||||
List<ItemData> itemDatas = GetItemDataByDropIdAndType(t);
|
||||
if (itemDatas != null && itemDatas.Count > 0)
|
||||
{
|
||||
itemDataList.AddRange(itemDatas);
|
||||
}
|
||||
}
|
||||
//itemDataList不需要重复合并
|
||||
if (itemDataList.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
LureInflation(itemDataList, inflate);
|
||||
if (isShow)
|
||||
{
|
||||
game.PlayerDataStorage.PlayerSaveAction.Present(() => GContext.Publish(new ShowData(itemDataList, rewardType)));
|
||||
}
|
||||
AddItem(itemDataList, changeSource);
|
||||
return itemDataList;
|
||||
}
|
||||
/// <summary>
|
||||
/// 对 itemDataList 中的体力进行膨胀
|
||||
/// </summary>
|
||||
/// <param name="itemDataList"></param>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
public void LureInflation(List<ItemData> itemDataList, float? inflate)
|
||||
{
|
||||
if (inflate == null)
|
||||
{
|
||||
inflate = GContext.container.Resolve<PlayerData>().InflationRate;//等级膨胀系数
|
||||
}
|
||||
for (int i = 0; i < itemDataList.Count; i++)
|
||||
{
|
||||
ItemData itemData = itemDataList[i];
|
||||
itemData.inflate = (float)inflate;
|
||||
if (itemData.id == 1001)
|
||||
{
|
||||
itemData.origin = itemData.count;
|
||||
itemData.count = IntInflation(itemData.count, inflate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 智能处理itemData.count的膨胀
|
||||
/// </summary>
|
||||
/// <param name="itemData"></param>
|
||||
/// <param name="inflate"></param>
|
||||
/// <returns>True if it needs inflation.</returns>
|
||||
public bool LureInflation(ItemData itemData, float? inflate)
|
||||
{
|
||||
if (itemData.id != 1001)
|
||||
return false;
|
||||
ItemInflation(itemData, inflate);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对itemData.count进行膨胀
|
||||
/// </summary>
|
||||
/// <param name="itemData"></param>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
public void ItemInflation(ItemData itemData, float? inflate)
|
||||
{
|
||||
itemData.origin = itemData.count;
|
||||
if (inflate == null)
|
||||
{
|
||||
inflate = GContext.container.Resolve<PlayerData>().InflationRate;//等级膨胀系数
|
||||
}
|
||||
itemData.inflate = (float)inflate;
|
||||
itemData.count = IntInflation(itemData.count, inflate);
|
||||
}
|
||||
/// <summary>
|
||||
/// 对数字进行膨胀
|
||||
/// </summary>
|
||||
/// <param name="origin"></param>
|
||||
/// <param name="inflate">体力膨胀系数 == null 当前实时系数</param>
|
||||
/// <returns></returns>
|
||||
public int IntInflation(int origin, float? inflate)
|
||||
{
|
||||
if (inflate == null)
|
||||
{
|
||||
inflate = GContext.container.Resolve<PlayerData>().InflationRate;//等级膨胀系数
|
||||
}
|
||||
if (inflate > 0)
|
||||
{
|
||||
float count = origin * (1 + inflate.Value);
|
||||
var roundingStep = _tables.TbGlobalConfig.LureScaleRoundingStep;
|
||||
int stepCount = roundingStep.Count / 2;
|
||||
int step = roundingStep[stepCount * 2];
|
||||
for (int i = 0; i < stepCount; i++)
|
||||
{
|
||||
if (count <= roundingStep[i])
|
||||
{
|
||||
step = roundingStep[i + stepCount];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float a = count / step;
|
||||
var b = Math.Round(a, MidpointRounding.AwayFromZero);
|
||||
origin = (int)b * step;
|
||||
}
|
||||
return origin;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public class TotalRechargeData
|
||||
{
|
||||
public int eventID;
|
||||
public int progress;
|
||||
public int vip;
|
||||
public int oldProgress;
|
||||
}
|
||||
public class TotalRechargeManager
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public const string TotalRechargeDataKey = "EventTotalRecharge";
|
||||
public TotalRechargeData GetEventData()
|
||||
{
|
||||
var totalRechargeData = new TotalRechargeData();
|
||||
|
||||
string data = saveStore.Read(TotalRechargeDataKey).ValueOrThrow();
|
||||
if (data != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
totalRechargeData = Newtonsoft.Json.JsonConvert.DeserializeObject<TotalRechargeData>(data) ?? throw new Newtonsoft.Json.JsonSerializationException("Null TotalRechargeData save.");
|
||||
if (totalRechargeData.vip > 10)
|
||||
{
|
||||
totalRechargeData.vip = 10;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
FishingEvent tr = GContext.container.Resolve<FishingEventData>().GetEventByTypeAndSubType(12, 1);
|
||||
|
||||
if (tr != null && totalRechargeData.eventID != tr.ID)
|
||||
{
|
||||
totalRechargeData = new TotalRechargeData();
|
||||
totalRechargeData.eventID = tr.ID;
|
||||
totalRechargeData.vip = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
if (totalRechargeData.vip > 10)
|
||||
{
|
||||
totalRechargeData.vip = 10;
|
||||
}
|
||||
SaveTotalRechargeData(totalRechargeData);
|
||||
}
|
||||
return totalRechargeData;
|
||||
}
|
||||
|
||||
public void GetTotalReward(RewardType rewardType, string textValue)
|
||||
{
|
||||
var totalRechargeData = GetEventData();
|
||||
List<int> rewardIDs = new List<int>();
|
||||
var tables = GContext.container.Resolve<Tables>();
|
||||
var eventTotalRecharge = GetEventTotalRecharge(totalRechargeData);
|
||||
var taskIds = eventTotalRecharge.TaskList;
|
||||
var vip = totalRechargeData.vip;
|
||||
int oldProgress = totalRechargeData.oldProgress;
|
||||
int newProgress = totalRechargeData.progress;
|
||||
int taskCount = taskIds.Count;
|
||||
for (int i = 0; i < taskCount; i++)
|
||||
{
|
||||
int taskId = taskIds[i];
|
||||
CommonTask commonTask = tables.TbCommonTask.GetOrDefault(taskId);
|
||||
if (commonTask == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int iapid = commonTask.TaskParam[vip];
|
||||
var iap = tables.TbIAPItemList.GetOrDefault(iapid);
|
||||
if (oldProgress < iap.VIPPoint && newProgress >= iap.VIPPoint)
|
||||
{
|
||||
int rewardID = commonTask.TaskReward[vip];
|
||||
rewardIDs.Add(rewardID);
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("event_totalrecharge"))
|
||||
{
|
||||
e.AddContent("task_id", taskId)
|
||||
.AddContent("reward_id", rewardID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (rewardIDs.Count > 0)
|
||||
{
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
var itemData = playerItemData.GetItemByDropListLureInflation(null, rewardIDs);
|
||||
var showData = new ShowData(itemData, rewardType);
|
||||
showData.textValue = textValue;
|
||||
GContext.Publish(showData);
|
||||
playerItemData.AddItem(itemData, null);
|
||||
}
|
||||
totalRechargeData.oldProgress = totalRechargeData.progress;
|
||||
SaveTotalRechargeData(totalRechargeData);
|
||||
}
|
||||
|
||||
public void AddProgress(int progress)
|
||||
{
|
||||
TotalRechargeData totalRechargeData = GetEventData();
|
||||
if (CheckTotalRechargeOpen(totalRechargeData))
|
||||
{
|
||||
totalRechargeData.progress += progress;
|
||||
SaveTotalRechargeData(totalRechargeData);
|
||||
}
|
||||
}
|
||||
public bool CheckTotalRechargeOpen()
|
||||
{
|
||||
TotalRechargeData totalRechargeData = GetEventData();
|
||||
return CheckTotalRechargeOpen(totalRechargeData);
|
||||
}
|
||||
EventTotalRecharge GetEventTotalRecharge(TotalRechargeData totalRechargeData)
|
||||
{
|
||||
if (totalRechargeData == null || totalRechargeData.eventID == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var _tables = GContext.container.Resolve<Tables>();
|
||||
FishingEvent fishingEvent = _tables.TbFishingEvent.GetOrDefault(totalRechargeData.eventID);
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return _tables.TbEventTotalRecharge[fishingEvent.RedirectID];
|
||||
}
|
||||
public bool CheckTotalRechargeOpen(TotalRechargeData totalRechargeData)
|
||||
{
|
||||
if (totalRechargeData == null || totalRechargeData.eventID == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var _tables = GContext.container.Resolve<Tables>();
|
||||
FishingEvent fishingEvent = _tables.TbFishingEvent.GetOrDefault(totalRechargeData.eventID);
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LimitedTime limitedTime = fishingEvent.TimeDefinition as LimitedTime;
|
||||
var startTime = DateTime.Parse(limitedTime.StartTime);
|
||||
var endTime = DateTime.Parse(limitedTime.EndTime);
|
||||
var curTime = ZZTimeHelper.UtcNow();
|
||||
if (curTime < startTime || curTime > endTime)
|
||||
{
|
||||
if (totalRechargeData.oldProgress != totalRechargeData.progress)
|
||||
{
|
||||
RewardType rewardType = RewardType.FishBoxFlyRestore;
|
||||
string textValue = LocalizationMgr.GetText("UI_FishingRewardPanel_101042");
|
||||
GetTotalReward(rewardType, textValue);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
var eventTotalRecharge = GetEventTotalRecharge(totalRechargeData);
|
||||
var taskIds = eventTotalRecharge.TaskList;
|
||||
var vip = totalRechargeData.vip;
|
||||
int oldProgress = totalRechargeData.oldProgress;
|
||||
int taskCount = taskIds.Count;
|
||||
int taskId = taskIds[^1];
|
||||
CommonTask commonTask = _tables.TbCommonTask.GetOrDefault(taskId);
|
||||
if (commonTask == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int iapid = commonTask.TaskParam[vip];
|
||||
var iap = _tables.TbIAPItemList.GetOrDefault(iapid);
|
||||
|
||||
return totalRechargeData.oldProgress < iap.VIPPoint;
|
||||
}
|
||||
|
||||
void SaveTotalRechargeData(TotalRechargeData totalRechargeData)
|
||||
{
|
||||
saveStore.Write(TotalRechargeDataKey, JsonMapper.ToJson(totalRechargeData)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,942 @@
|
||||
using asap.core;
|
||||
using game;
|
||||
using GameCore;
|
||||
using IngameDebugConsole;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UI.HookWheel;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class DebugFunc
|
||||
{
|
||||
public static void Init()
|
||||
{
|
||||
var config = GContext.container.Resolve<IConfig>();
|
||||
if (config != null && (config.Get("DEBUG", "0") == "1" || config.Get("GM", "0") == "1"))
|
||||
{
|
||||
DebugLogConsole.AddCommand<string, string>("Go.show", "Shows a GameObject", ShowGO);
|
||||
DebugLogConsole.AddCommand<string, string>("Go.hide", "Shows a GameObject", HideGO);
|
||||
|
||||
DebugLogConsole.AddCommand<int, string>("Render.quality", "change quality", SetQuality);
|
||||
DebugLogConsole.AddCommand<float, string>("Render.scale", "Set render scale [0.1-2]", SetRendererScale);
|
||||
DebugLogConsole.AddCommand<int, string>("Render.aa", "anti aliasing", SetAntiAliasing);
|
||||
DebugLogConsole.AddCommand<string>("Render.ShadowToggle", "Toggle Shadow", ToggleShadow);
|
||||
DebugLogConsole.AddCommand<string, string>("Render.Pipline", "light num", SetRenderPipline);
|
||||
|
||||
DebugLogConsole.AddCommand<string>("info.GPU", "GPU Info", GpuInfo);
|
||||
DebugLogConsole.AddCommand<string>("info.CPU", "CPU Info", CpuInfo);
|
||||
DebugLogConsole.AddCommand<string>("info.Support", "Device Support", SupportInfo);
|
||||
|
||||
DebugLogConsole.AddCommand("FPS", "Toggle FPS", Fps);
|
||||
DebugLogConsole.AddCommand("LTPL", "开关 Leviathan Timeline 性能日志(不影响 FPS 浮层)", ToggleLeviathanTimelinePerf);
|
||||
DebugLogConsole.AddCommand<int>("LTCW", "设置 CatchUp 消费等待(ms) 1~50", SetLeviathanCatchUpWaitMs);
|
||||
DebugLogConsole.AddCommand("LTCW?", "查询 CatchUp 消费等待(ms)", GetLeviathanCatchUpWaitMs);
|
||||
|
||||
DebugLogConsole.AddCommand<int>("Render.antiAliasing", "change antiAliasing", SetRTAntiAliasing);
|
||||
DebugLogConsole.AddCommand<string>("T.hotupdate", "hot update test", HotUpdateTest);
|
||||
DebugLogConsole.AddCommand<string, string>("UI.hide", "Hide UI", ToggleUI);
|
||||
DebugLogConsole.AddCommand<int, string>("Misc.tg", "Hide misc", ToggleMisc);
|
||||
DebugLogConsole.AddCommand("aihelp", "open aihelp", OpenAIHelp);
|
||||
DebugLogConsole.AddCommand("review", "open review", OpenReview);
|
||||
|
||||
DebugLogConsole.AddCommand("Pay.sku", "sku list", GetSKUs);
|
||||
DebugLogConsole.AddCommand("ad.debug", "Active Ad Debug", ActiveAdDebug);
|
||||
DebugLogConsole.AddCommand("ad.media", "Active Ad Media Debug", ActiveAdMedia);
|
||||
|
||||
DebugLogConsole.AddCommand("notify.send", "Local Notify Test", NotifyTest);
|
||||
DebugLogConsole.AddCommand("notify.setting", " Notify set", NotifySettingTest);
|
||||
DebugLogConsole.AddCommand<string>("notify.status", " Notify Status", NotificationStatus);
|
||||
DebugLogConsole.AddCommand<string>("notify.time", "Notify at certain time", NotifyAtTime);
|
||||
|
||||
|
||||
DebugLogConsole.AddCommand("mail.status", "Print cache mail", PrintMail);
|
||||
DebugLogConsole.AddCommand("mail.fetch", "fetch mail to cache", FetchMail);
|
||||
DebugLogConsole.AddCommand("rt.test", "test realtime", RtTest);
|
||||
|
||||
DebugLogConsole.AddCommand<int>("account.loginTime", "login time - 1day", LoginTime);
|
||||
DebugLogConsole.AddCommand("account.info", "account info", AccoutInfo);
|
||||
DebugLogConsole.AddCommand("account.authortest", "author test", AuthorTest);
|
||||
|
||||
DebugLogConsole.AddCommand("aihelp.test", "aihelp test", AIHelpTest);
|
||||
|
||||
DebugLogConsole.AddCommand("open.washing", "Open Washing Activity", OpenWashingActivity);
|
||||
|
||||
DebugLogConsole.AddCommand("deeplink.test", "Test if deeplink connects. If so, returns pid and uid.", TestDeepLink);
|
||||
|
||||
DebugLogConsole.AddCommand<int>("PotionGoTo", "Potion Go To", PotionGoTo);
|
||||
DebugLogConsole.AddCommand("potion.clear", "Potion Go To", PotionClear);
|
||||
|
||||
DebugLogConsole.AddCommand("pinball.clear", "Clear Pinball Data", PinballDataClear);
|
||||
DebugLogConsole.AddCommand<float>("pinball.fire", "Launch Pinball Pellet", PinballFirePellet);
|
||||
|
||||
DebugLogConsole.AddCommand("scratch.clear", "Clear Scratch Ticket Data", ScratchTicketDataClear);
|
||||
|
||||
DebugLogConsole.AddCommand<int>("shooting.go", "Goto", ShootingRangeDebug);
|
||||
DebugLogConsole.AddCommand("eventpartner.activate", "Activate a temp event", EventPartnerDebugActivate);
|
||||
DebugLogConsole.AddCommand("bingo.ts", "OpenTestScene", ShowTestBingoPanel);
|
||||
DebugLogConsole.AddCommand("bingo.seeds", "TestBingoSeedPickingRng", TestBingoSeedPickingRng);
|
||||
DebugLogConsole.AddCommand("bingo.instant", "Instant Bingo.", TestBingoInstantBingo);
|
||||
|
||||
DebugLogConsole.AddCommand("srp.fswave", "toggle fullscreen wave", ToggleFSWave);
|
||||
DebugLogConsole.AddCommand<int>("ib.lvl", "infinitebuilding level", SetInfiniteBuildLvl);
|
||||
DebugLogConsole.AddCommand("srp.fsblur", "toggle fullscreen blur", ToggleFSBlur);
|
||||
|
||||
DebugLogConsole.AddCommand<string>("stash", "test reward stash service", TestRewardStashService);
|
||||
DebugLogConsole.AddCommand("guide.clear", "clear guide data", ClearGuide);
|
||||
DebugLogConsole.AddCommand("pinataAct", "load pinata Act", LoadPinataAct);
|
||||
DebugLogConsole.AddCommand("lastpayment.clear", "Clear Last Payment Time", ClearLastPaymentTime);
|
||||
DebugLogConsole.AddCommand("HookWheelForecActive", "Hook Wheel Forec Active", HookWheelForecActive);
|
||||
|
||||
DebugLogConsole.AddCommand("eps.invitecount", "GetInvitationCount", PrintEventPartnerStatueInvitationCount);
|
||||
DebugLogConsole.AddCommand("eps.sendbot", "Start recommending bots", EventPartnerStatueActivateBotInvitation);
|
||||
DebugLogConsole.AddCommand("eps.bot", "Peek Robot Data", EventPartnerStatuePeekRobot);
|
||||
DebugLogConsole.AddCommand("eps.pool", "Peek Player Info Pool", EventPartnerStatuePeekPlayerInfoPool);
|
||||
DebugLogConsole.AddCommand("eps.score", "Peek Score", EventPartnerStatuePeekScore);
|
||||
DebugLogConsole.AddCommand<int>("eps.rt", "Test rt", EventPartnerStatueTestRt);
|
||||
DebugLogConsole.AddCommand("eps.i", "Peek inflationrate", EventPartnerStatuePeekInflationRate);
|
||||
|
||||
DebugLogConsole.AddCommand<int>("sr.set", "Set Social Rush Progress", SocialRushTest);
|
||||
DebugLogConsole.AddCommand("sr.reset", "Reset Social Rush", SocialRushReset);
|
||||
DebugLogConsole.AddCommand("sr.peek", "Peek Social Rush", SocialRushPeek);
|
||||
DebugLogConsole.AddCommand("mft.guide", "Mow For Treasure Clear Guide", EventMowForTreasureGuideClear);
|
||||
|
||||
DebugLogConsole.AddCommand("osc.peek", "Peek Offer Story Chain", OfferStoryChainPeek);
|
||||
DebugLogConsole.AddCommand("osc.reset", "Reset Offer Story Chain", OfferStoryChainReset);
|
||||
|
||||
DebugLogConsole.AddCommand("ota.peek", "Peek Offer treasure ascent", OfferTreasureAscentPeek);
|
||||
DebugLogConsole.AddCommand("ota.reset", "Reset Offer treasure ascent", OfferTreasureAscentReset);
|
||||
|
||||
DebugLogConsole.AddCommand<int>("pu.bk", "Block Pinball Kick if input is not 0. If input is 0, unblock.", PinballUncountableBlockKick);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TestDeepLink()
|
||||
{
|
||||
var res = AFHelper.Instance.GetDLReferer(out var pid, out var uid);
|
||||
Debug.Log($"DidReceiveDeepLink: {AFHelper.Instance.DidReceivedDeepLink}");
|
||||
Debug.Log($"IsFirstLaunch: {AFHelper.Instance.IsFirstLaunch}");
|
||||
|
||||
if (AFHelper.Instance.DeepLinkParamsDictionary is not null)
|
||||
{
|
||||
Debug.Log($"DeepLinkParamsDic:");
|
||||
foreach (var kvp in AFHelper.Instance.DeepLinkParamsDictionary)
|
||||
{
|
||||
Debug.Log($"{kvp.Key}: {kvp.Value}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"DeepLinkParamsDic: Empty.");
|
||||
}
|
||||
var message = res ? $"Deeplink success with pid: {pid}, uid: {uid}" : "Deeplink fail.";
|
||||
Debug.Log(message);
|
||||
}
|
||||
|
||||
private static void NotifyAtTime(string time)
|
||||
{
|
||||
var now = ZZTimeHelper.UtcNow();
|
||||
var fireTime = DateTime.SpecifyKind(DateTime.Parse(time), DateTimeKind.Utc);
|
||||
fireTime = fireTime.AddDays(now <= fireTime ? 0 : 1);
|
||||
var service = GContext.container.Resolve<ILocalNotificationService>();
|
||||
service.ScheduleOneTimeNotification(434200, fireTime.ToLocalTime(), "Test Note at Time",
|
||||
$"Test Notification @ {fireTime}.");
|
||||
}
|
||||
|
||||
private static void NotifyTest()
|
||||
{
|
||||
ILocalNotificationService service = GContext.container.Resolve<ILocalNotificationService>();
|
||||
service.ScheduleOneTimeNotification(434200, DateTime.Now + TimeSpan.FromSeconds(3), "Notification",
|
||||
"Test Notification ");
|
||||
}
|
||||
|
||||
private static void NotifySettingTest()
|
||||
{
|
||||
ILocalNotificationService service = GContext.container.Resolve<ILocalNotificationService>();
|
||||
service.OpenSettings();
|
||||
}
|
||||
|
||||
private static string NotificationStatus()
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
return Unity.Notifications.Android.AndroidNotificationCenter.UserPermissionToPost.ToString();
|
||||
#else
|
||||
return Unity.Notifications.iOS.iOSNotificationCenter.GetNotificationSettings().AuthorizationStatus.ToString();
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string HotUpdateTest()
|
||||
{
|
||||
return "hot update test Ver 0.6 - 2024-03-26:1434";
|
||||
}
|
||||
|
||||
private static string ToggleUI(string path)
|
||||
{
|
||||
GameObject homePanelGo = UIManager.Instance.gameObject.transform.Find("HomePanel").gameObject;
|
||||
if (homePanelGo == null)
|
||||
{
|
||||
return "UI not found.";
|
||||
}
|
||||
|
||||
GameObject go = homePanelGo.transform.Find(path).gameObject;
|
||||
if (go == null)
|
||||
{
|
||||
return "path not found";
|
||||
}
|
||||
|
||||
go.SetActive(!go.activeSelf);
|
||||
return $"UI {go} toggled to {go.activeSelf}.";
|
||||
}
|
||||
|
||||
private static string ToggleMisc(int idx)
|
||||
{
|
||||
GameObject go = GetGOFromPath("Env_101(Clone)/Misc");
|
||||
int childCount = go.transform.childCount;
|
||||
if (idx >= childCount)
|
||||
return $"index {idx} exceeds children count{childCount}";
|
||||
var child = go.transform.GetChild(idx).gameObject;
|
||||
if (child == null)
|
||||
return $"child {idx} not found";
|
||||
child.SetActive(!child.activeSelf);
|
||||
return $"{child} toggled to {child.activeSelf}.";
|
||||
}
|
||||
|
||||
private static void OpenAIHelp()
|
||||
{
|
||||
AIHelp.AIHelpSupport.Show("E001");
|
||||
}
|
||||
|
||||
private static void OpenReview()
|
||||
{
|
||||
var config = GContext.container.Resolve<IConfig>();
|
||||
tysdk.AppRequestReview.RequestReview(config.Get(GConstant.K_APP_Store_URL, string.Empty));
|
||||
}
|
||||
|
||||
private static string ShowGO(string path)
|
||||
{
|
||||
GameObject go = GetGOFromPath(path);
|
||||
if (go != null)
|
||||
{
|
||||
go.SetActive(true);
|
||||
}
|
||||
|
||||
return $"GameObject not found";
|
||||
}
|
||||
|
||||
private static string HideGO(string path)
|
||||
{
|
||||
GameObject go = GetGOFromPath(path);
|
||||
if (go != null)
|
||||
{
|
||||
go.SetActive(false);
|
||||
return $"{go.name} has been hidden";
|
||||
}
|
||||
|
||||
return $"GameObject not found";
|
||||
}
|
||||
|
||||
private static string SetQuality(int level)
|
||||
{
|
||||
QualityManager.ChangeQuality(level);
|
||||
zzwater.URPHelper.RendererFeatureSetActive(false, "FSBlur");
|
||||
zzwater.URPHelper.RendererFeatureSetActive(false, "FSWave");
|
||||
return $"quality set to {QualitySettings.names[QualitySettings.GetQualityLevel()]}";
|
||||
}
|
||||
|
||||
private static void SetRTAntiAliasing(int level)
|
||||
{
|
||||
var rt = new RenderTexture((int)125, (int)125, 24, RenderTextureFormat.ARGB32);
|
||||
rt.antiAliasing = level;
|
||||
}
|
||||
|
||||
private static string SetRendererScale(float ratio)
|
||||
{
|
||||
if (ratio == 0)
|
||||
{
|
||||
return $"Render scale {CurrentRenderPipeline.renderScale}";
|
||||
}
|
||||
|
||||
ratio = Mathf.Clamp(ratio, 0.1f, 2f);
|
||||
var uprAsset = CurrentRenderPipeline;
|
||||
if (uprAsset == null) return "No UniversalRenderPipelineAsset";
|
||||
var preValue = uprAsset.renderScale;
|
||||
uprAsset.renderScale = ratio;
|
||||
return $"Render scale {preValue} set to {ratio}";
|
||||
}
|
||||
|
||||
private static string SetAntiAliasing(int count)
|
||||
{
|
||||
var uprAsset = CurrentRenderPipeline;
|
||||
if (uprAsset == null) return "No UniversalRenderPipelineAsset";
|
||||
var preValue = uprAsset.msaaSampleCount;
|
||||
uprAsset.msaaSampleCount = count;
|
||||
return $"Render scale {preValue} set to {count}";
|
||||
}
|
||||
|
||||
private static string SetRenderPipline(string name)
|
||||
{
|
||||
var currentPiplineName = GraphicsSettings.currentRenderPipeline.name;
|
||||
if (name == "?")
|
||||
{
|
||||
return $"Pipline set to {currentPiplineName}";
|
||||
}
|
||||
else if (name == "-")
|
||||
{
|
||||
QualitySettings.renderPipeline = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var pipelineAsset = Addressables.LoadAssetAsync<RenderPipelineAsset>(name).WaitForCompletion();
|
||||
QualitySettings.renderPipeline = pipelineAsset;
|
||||
}
|
||||
|
||||
var newPipelineName = GraphicsSettings.currentRenderPipeline.name;
|
||||
return $"{currentPiplineName} set to {newPipelineName}";
|
||||
}
|
||||
|
||||
|
||||
private static string ToggleShadow()
|
||||
{
|
||||
var cam = Camera.main;
|
||||
if (cam != null)
|
||||
{
|
||||
var comp = cam.GetComponent<UniversalAdditionalCameraData>();
|
||||
var preValue = comp.renderShadows;
|
||||
comp.renderShadows = !preValue;
|
||||
return $"Shadow {preValue} set to {comp.renderShadows}";
|
||||
}
|
||||
|
||||
return "Camera not found";
|
||||
}
|
||||
|
||||
private static string CpuInfo()
|
||||
{
|
||||
var props = typeof(SystemInfo).GetProperties()
|
||||
.Where(x => x.Name.StartsWith("processor"));
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine();
|
||||
foreach (var prop in props)
|
||||
{
|
||||
sb.AppendLine($"{prop.Name}: {prop.GetValue(null)}");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GpuInfo()
|
||||
{
|
||||
var props = typeof(SystemInfo).GetProperties()
|
||||
.Where(x => x.Name.StartsWith("graphics"));
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine();
|
||||
foreach (var prop in props)
|
||||
{
|
||||
sb.AppendLine($"{prop.Name}: {prop.GetValue(null)}");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string SupportInfo()
|
||||
{
|
||||
var props = typeof(SystemInfo).GetProperties()
|
||||
.Where(x => x.Name.StartsWith("support"));
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine();
|
||||
foreach (var prop in props)
|
||||
{
|
||||
sb.AppendLine($"{prop.Name}: {prop.GetValue(null)}");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void Fps()
|
||||
{
|
||||
game.RootCtx.showFPS = !game.RootCtx.showFPS;
|
||||
}
|
||||
|
||||
private static void ToggleLeviathanTimelinePerf()
|
||||
{
|
||||
var on = !LeviathanLogConfig.EnableTimelineVideoPerfLog;
|
||||
LeviathanLogConfig.EnableTimelineVideoPerfLog = on;
|
||||
PlayerPrefs.SetInt("LeviathanTimelinePerf", on ? 1 : 0);
|
||||
PlayerPrefs.Save();
|
||||
UnityEngine.Debug.Log($"[Debug] Leviathan Timeline 性能日志 = {on}(Logcat 搜 LeviathanTimeline)");
|
||||
}
|
||||
|
||||
private static void SetLeviathanCatchUpWaitMs(int ms)
|
||||
{
|
||||
var decoders = UnityEngine.Object.FindObjectsOfType<LeviathanVideoDecoderBase>(true);
|
||||
if (decoders == null || decoders.Length == 0)
|
||||
{
|
||||
UnityEngine.Debug.Log("[Debug] LTCW: 未找到活跃的 LeviathanVideoDecoderBase 实例");
|
||||
return;
|
||||
}
|
||||
foreach (var d in decoders)
|
||||
{
|
||||
d.CatchUpConsumePerFrameWaitMs = ms;
|
||||
}
|
||||
UnityEngine.Debug.Log($"[Debug] LTCW: 已将 {decoders.Length} 个解码器 CatchUpConsumePerFrameWaitMs 设为 {ms}ms");
|
||||
}
|
||||
|
||||
private static void GetLeviathanCatchUpWaitMs()
|
||||
{
|
||||
var decoders = UnityEngine.Object.FindObjectsOfType<LeviathanVideoDecoderBase>();
|
||||
if (decoders == null || decoders.Length == 0)
|
||||
{
|
||||
UnityEngine.Debug.Log("[Debug] LTCW?: 未找到活跃的 LeviathanVideoDecoderBase 实例");
|
||||
return;
|
||||
}
|
||||
foreach (var d in decoders)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[Debug] LTCW? {d.name}: CatchUpConsumePerFrameWaitMs={d.CatchUpConsumePerFrameWaitMs}ms");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static UniversalRenderPipelineAsset CurrentRenderPipeline
|
||||
{
|
||||
get
|
||||
{
|
||||
return GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
|
||||
//return (UniversalRenderPipelineAsset) QualitySettings.renderPipeline;
|
||||
}
|
||||
}
|
||||
|
||||
private static async void GetSKUs()
|
||||
{
|
||||
var iapService = GContext.container.Resolve<game.IIAPService>();
|
||||
if (iapService == null)
|
||||
{
|
||||
UnityEngine.Debug.Log("iapService is null");
|
||||
;
|
||||
return;
|
||||
}
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine();
|
||||
var skus = await iapService.GetSKUs();
|
||||
foreach (var sku in skus)
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.Append($" {sku.price} code {sku.price_currency_code} ");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
UnityEngine.Debug.Log(sb.ToString());
|
||||
}
|
||||
|
||||
private static void ActiveAdDebug()
|
||||
{
|
||||
MaxSdk.SetVerboseLogging(true);
|
||||
MaxSdk.ShowCreativeDebugger();
|
||||
}
|
||||
|
||||
private static void ActiveAdMedia()
|
||||
{
|
||||
MaxSdk.ShowMediationDebugger();
|
||||
}
|
||||
|
||||
private static GameObject GetGOFromPath(string path)
|
||||
{
|
||||
var root = SceneManager.GetActiveScene().GetRootGameObjects();
|
||||
var splitPath = path.Split('/');
|
||||
var rootName = splitPath[0];
|
||||
var rootGo = root.FirstOrDefault(x => x.name == rootName);
|
||||
if (rootGo == null) return null;
|
||||
if (splitPath.Length == 1) return rootGo;
|
||||
var childPath = string.Join("/", splitPath.Skip(1));
|
||||
return rootGo.transform.Find(childPath)?.gameObject;
|
||||
}
|
||||
|
||||
private static void PrintMail()
|
||||
{
|
||||
var mailService = GContext.container.Resolve<IInGameMailService>() as InGameMailService;
|
||||
mailService.PrintCacheMail();
|
||||
}
|
||||
|
||||
private static void FetchMail()
|
||||
{
|
||||
GContext.container.Resolve<IInGameMailService>().FetchMail();
|
||||
}
|
||||
|
||||
private static void RtTest()
|
||||
{
|
||||
var rtService = GContext.container.Resolve<IRTService>() as SignalRService;
|
||||
rtService.Test();
|
||||
}
|
||||
|
||||
private static void LoginTime(int hours)
|
||||
{
|
||||
var userService = GContext.container.Resolve<game.IUserService>() as game.PlayFabUserService;
|
||||
if (userService == null) return;
|
||||
|
||||
var loginTime = userService.LoginTime;
|
||||
var newLoginTime = loginTime.AddHours(hours);
|
||||
var prop = userService.GetType().GetProperty("LoginTime");
|
||||
prop.SetValue(userService, newLoginTime);
|
||||
Debug.Log($"loginTime: {loginTime} newLoginTime: {newLoginTime} prop: {userService.LoginTime}");
|
||||
}
|
||||
|
||||
private static void AccoutInfo()
|
||||
{
|
||||
var userService = GContext.container.Resolve<game.IUserService>() as game.PlayFabUserService;
|
||||
if (userService == null) return;
|
||||
Debug.Log($"pfid: {userService.UserId} tyid: {userService.CustomId} lastLogin: {userService.LoginTime}");
|
||||
|
||||
if (PlayerPrefs.HasKey("KEY_ACCOUNT_INFO"))
|
||||
{
|
||||
Debug.Log("Saved account info: " + PlayerPrefs.GetString("KEY_ACCOUNT_INFO"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("No saved account info");
|
||||
}
|
||||
}
|
||||
|
||||
private static async void AuthorTest()
|
||||
{
|
||||
var svr = GContext.container.Resolve<ICustomServerMgr>();
|
||||
var result = await svr.GetStringAsync("account/authortest");
|
||||
Debug.Log($"AuthorTest {result}");
|
||||
}
|
||||
|
||||
private static void AIHelpTest()
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.AIHelp_Msg, true, 1);
|
||||
}
|
||||
|
||||
private static void OpenWashingActivity()
|
||||
{
|
||||
ToggleUI("safearea/RightPanel/layout/btn_washing");
|
||||
}
|
||||
|
||||
private static void PotionClear()
|
||||
{
|
||||
GContext.container.Resolve<EventPotionDataManager>().ClearPotionData();
|
||||
Debug.Log("--- Potion Clear ok");
|
||||
}
|
||||
|
||||
private static void PotionGoTo(int levelID)
|
||||
{
|
||||
GContext.container.Resolve<EventPotionDataManager>().GoToPotionLevel(levelID);
|
||||
Debug.Log("--- PotionGoTo levelID:" + levelID + ", ok");
|
||||
}
|
||||
|
||||
private static void PinballDataClear()
|
||||
{
|
||||
GContext.container.Resolve<EventPinballDataManager>().ClearData();
|
||||
Debug.Log("--- PinballDataClear ok");
|
||||
}
|
||||
|
||||
private static void PinballFirePellet(float force)
|
||||
{
|
||||
FishingPinballAct.Publish(new EventPinballLaunchPelletData()
|
||||
{
|
||||
Force = force
|
||||
});
|
||||
Debug.Log("--- PinballFirePellet ok, force:" + force);
|
||||
}
|
||||
|
||||
private static void ScratchTicketDataClear()
|
||||
{
|
||||
GContext.container.Resolve<EventScratchTicketDataManager>().ClearData();
|
||||
Debug.Log("--- Scratch Ticket Data Clear ok");
|
||||
}
|
||||
|
||||
private static void ShootingRangeDebug(int n)
|
||||
{
|
||||
GContext.Publish(new EventShootingRangeDebug { StageIdx = n });
|
||||
Debug.Log($"Switch to {n}.");
|
||||
}
|
||||
|
||||
private static void EventPartnerDebugActivate()
|
||||
{
|
||||
// var d = GContext.container.Resolve<EventPartnerData>();
|
||||
// if (d is null)
|
||||
// {
|
||||
// Debug.Log( $"<color=#2d7cee>[EventPartner] No data found, cannot activate.</color>");
|
||||
// return;
|
||||
// }
|
||||
// _ = d.LoadDataDebug();
|
||||
Debug.Log($"<color=#2d7cee>[EventPartner] This function is obsoleted for now.</color>");
|
||||
}
|
||||
|
||||
private static void ToggleFSWave()
|
||||
{
|
||||
var feature = zzwater.URPHelper.FindAndCacheRendererFeature("FSWave");
|
||||
if (feature == null)
|
||||
Debug.LogError($"Feature FSWave not found");
|
||||
else
|
||||
feature.SetActive(!feature.isActive);
|
||||
}
|
||||
|
||||
private static void SetInfiniteBuildLvl(int level)
|
||||
{
|
||||
var s = GContext.container.Resolve<PlayerData>().InfiniteBuildingData;
|
||||
if (s == null)
|
||||
{
|
||||
Debug.LogError($"No ib data found.");
|
||||
return;
|
||||
}
|
||||
var data = new InfiniteBuildingData();
|
||||
data.Deserialize(s);
|
||||
var delta = level - data.Lvl;
|
||||
data.Lvl = level;
|
||||
for (int i = 0; i < data.Record.Count; i++)
|
||||
{
|
||||
var r = data.Record[i];
|
||||
r.Pos += Vector2.up * delta;
|
||||
data.Record[i] = r;
|
||||
}
|
||||
data.UploadData();
|
||||
Debug.Log($"Done.");
|
||||
}
|
||||
|
||||
private static void ToggleFSBlur()
|
||||
{
|
||||
var feature = zzwater.URPHelper.FindAndCacheRendererFeature("FSBlur");
|
||||
if (feature == null)
|
||||
Debug.LogError($"Feature FSWave not found");
|
||||
else
|
||||
feature.SetActive(!feature.isActive);
|
||||
}
|
||||
|
||||
private static void ShowTestBingoPanel()
|
||||
{
|
||||
GContext.container.Register<EventBingoModel>().AsSingleton();
|
||||
GContext.container.Resolve<EventBingoModel>().InitTest();
|
||||
GContext.Publish(new UnloadActToNextAct { actId = "EventBingoAct", TransitionPanel = UITypes.CloudTransitionPanel });
|
||||
}
|
||||
|
||||
private static void TestBingoSeedPickingRng()
|
||||
{
|
||||
var dataList = GContext.container.Resolve<cfg.Tables>().TbEventBingoMain.DataList;
|
||||
var tc = new EventBingoTableContext(40701);
|
||||
int[] stat = { 0, 0, 0, 0 };
|
||||
int seed, statCount = 10000;
|
||||
var sb = new System.Text.StringBuilder();
|
||||
for (int i = 0; i < statCount; i++)
|
||||
{
|
||||
seed = tc.PickSeed(roundCount: 10086);
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
if (dataList[j].SeedID.Contains(seed))
|
||||
{
|
||||
stat[j]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sb.AppendLine($"{seed}, ");
|
||||
}
|
||||
Debug.Log($"[EventBingo] Test Result:\n1Bingo:{stat[0] / (float)statCount * 100}%,\n2Bingo: {stat[1] / (float)statCount * 100}%,\n3Bingo: {stat[2] / (float)statCount * 100}%,\n4Bingo: {stat[3] / (float)statCount * 100}%");
|
||||
Debug.Log("[EventBingo] Seeds: \n" + sb.ToString());
|
||||
}
|
||||
|
||||
private static void TestBingoInstantBingo()
|
||||
{
|
||||
var model = GContext.container.Resolve<EventBingoModel>();
|
||||
if (model == null)
|
||||
{
|
||||
Debug.Log("[EventBingo] Bingo model not found!");
|
||||
return;
|
||||
}
|
||||
model.ActivateInstantBingo();
|
||||
Debug.Log("[EventBingo] Instant Bingo activated.");
|
||||
}
|
||||
|
||||
private static void TestRewardStashService(string cmd)
|
||||
{
|
||||
const string reset = "reset", upload = "upload", load = "load", flush = "flush", add = "add";
|
||||
var service = GContext.container.Resolve<IDeferredRewardStashService>();
|
||||
switch (cmd)
|
||||
{
|
||||
case reset:
|
||||
service.Reset();
|
||||
Debug.Log("Reset Done.");
|
||||
break;
|
||||
case upload:
|
||||
service.UploadData();
|
||||
Debug.Log("Upload Done.");
|
||||
break;
|
||||
case load:
|
||||
service.LoadData();
|
||||
Debug.Log("Load Done.");
|
||||
break;
|
||||
case flush:
|
||||
service.Flush();
|
||||
Debug.Log("Flush Done.");
|
||||
break;
|
||||
case add:
|
||||
GContext.Publish(new DeferredRewardStashService.EventStashItem
|
||||
{
|
||||
Item = new ItemData(1001, 100)
|
||||
});
|
||||
Debug.Log("Add Done.");
|
||||
break;
|
||||
default:
|
||||
Debug.Log("Invalid command. Available commands:\n" +
|
||||
"- reset: Clear all stash data\n" +
|
||||
"- add: Add 1001 * 100\n" +
|
||||
"- upload: Upload stash data to playfab\n" +
|
||||
"- load: Load stash data from playfab\n" +
|
||||
"- flush: Process and clear all pending rewards");
|
||||
break;
|
||||
}
|
||||
}
|
||||
static void ClearGuide()
|
||||
{
|
||||
GContext.container.Resolve<GuideDataCenter>().GMClearGuide();
|
||||
UIManager.Instance.DestroyUI(UITypes.GuidancePanel);
|
||||
}
|
||||
static void LoadPinataAct()
|
||||
{
|
||||
GContext.Publish(new UnloadActToNextAct { actId = "PinataPackAct_Pony" });
|
||||
GContext.OnEvent<ActLoadedEvent>().Subscribe((actLoadedEvent) =>
|
||||
{
|
||||
var Act = GContext.container.ResolveAct("PinataPackAct_Pony");
|
||||
if (Act is PinataPackAct pinataPackAct)
|
||||
{
|
||||
Debug.Log($"jsd Act is PinataPackAct pinataPackAct");
|
||||
var rigidBody = pinataPackAct.GetComponentsInChildren<Rigidbody>();
|
||||
foreach (var r in rigidBody)
|
||||
{
|
||||
r.useGravity = false;
|
||||
}
|
||||
var gameObject = GetGOFromPath("PinataPackAct_Pony(Clone)/Stage1");
|
||||
gameObject.transform.localPosition = new Vector3(0, 0, 0.2f);
|
||||
gameObject.transform.localRotation = Quaternion.Euler(-21, 0, 0);
|
||||
}
|
||||
|
||||
var ui = UIManager.Instance.GetContainedUI(UITypes.PinataPack_PonySettlementPanel);
|
||||
ui.FindChildGameObject("btn_scene")?.SetActive(false);
|
||||
ui.FindChildGameObject("btn_close")?.SetActive(true);
|
||||
});
|
||||
}
|
||||
static void ClearLastPaymentTime()
|
||||
{
|
||||
GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>().Write("LastPaymentTime", "").RequireAccepted();
|
||||
}
|
||||
static void HookWheelForecActive()
|
||||
{
|
||||
GContext.container.Resolve<HookWheelManager>()?.ForceActive();
|
||||
}
|
||||
|
||||
static void PrintEventPartnerStatueInvitationCount()
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
Debug.Log($"InvitationCount: {arc.InvitationData.InvitationCount}, DoRecommendBot: {arc.InvitationData.DoRecommendBot}");
|
||||
}
|
||||
|
||||
static void EventPartnerStatueActivateBotInvitation()
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
arc.InvitationData.SetInvitationCount(1999);
|
||||
Debug.Log($"Bot invitation count set to 1999");
|
||||
}
|
||||
|
||||
// static void EventPartnerStatueRefreshInvitation()
|
||||
// {
|
||||
// var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
// if (arc == null)
|
||||
// {
|
||||
// Debug.Log("EventPartnerStatueArc not found");
|
||||
// return;
|
||||
// }
|
||||
// arc.InvitationData.RefreshInvitation();
|
||||
// Debug.Log("RefreshInvitation Done");
|
||||
// }
|
||||
|
||||
static void EventPartnerStatuePeekRobot()
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
var str = Newtonsoft.Json.JsonConvert.SerializeObject(arc.RobotData.RobotInvitationBuffer);
|
||||
Debug.Log("RobotBuffer: " + str);
|
||||
str = Newtonsoft.Json.JsonConvert.SerializeObject(arc.RobotData.RobotList.Select(kv => kv.Value.ActionQ));
|
||||
Debug.Log("RobotActionQ: " + str);
|
||||
}
|
||||
|
||||
static void EventPartnerStatuePeekPlayerInfoPool()
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
var bufferStr = Newtonsoft.Json.JsonConvert.SerializeObject(arc.PlayerInfoPool.PlayerInfoDic);
|
||||
Debug.Log("PlayerInfoCache:" + bufferStr);
|
||||
}
|
||||
|
||||
static void EventPartnerStatuePeekScore()
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
var s = new System.Text.StringBuilder();
|
||||
foreach (var c in arc.Model.ComponentList)
|
||||
{
|
||||
s.AppendLine($"Partner{c.PartnerId}: self display:{c.ScoreSelfDisplay}, self actual:{c.ScoreSelfActual}, partner display:{c.ScorePartnerDisplay}, partner actual:{c.ScorePartnerActual}\n");
|
||||
}
|
||||
Debug.Log(s.ToString());
|
||||
}
|
||||
|
||||
static void EventPartnerStatueTestRt(int scoreAdd)
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
var partnerId = arc.Model.ComponentList[0].PartnerId;
|
||||
var newScore = arc.Model.ComponentList[0].ScorePartnerActual + scoreAdd;
|
||||
var se = new EventPartnerGatherRequests.EventPartnerGatherScoreEvt
|
||||
{
|
||||
PartnerId = partnerId,
|
||||
Score = newScore,
|
||||
};
|
||||
var pe = new EventPartnerGatherRequests.EventPartnerGatherPartnerEvt
|
||||
{
|
||||
PartnerInfo = new PlayerInfo
|
||||
{
|
||||
AvatarUrl = "",
|
||||
DisplayName = "TestRt",
|
||||
Level = 1,
|
||||
PlayFabId = "TestRt",
|
||||
},
|
||||
EvtType = 0,
|
||||
SlotId = 1
|
||||
};
|
||||
arc.RtSystem.TestRt(se, pe);
|
||||
}
|
||||
|
||||
static void EventPartnerStatuePeekInflationRate()
|
||||
{
|
||||
var arc = GContext.container.Resolve<EventPartnerStatueArc>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("EventPartnerStatueArc not found");
|
||||
return;
|
||||
}
|
||||
Debug.Log($"InflationRate: {arc.Model.LureInflationRate}");
|
||||
}
|
||||
|
||||
static void SocialRushTest(int progress)
|
||||
{
|
||||
var arc = GContext.container.Resolve<Assets.Scripts.UI.SocialRush.SocialRushArchitecture>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("SocialRushArchitechture not found");
|
||||
return;
|
||||
}
|
||||
arc.Model.SetProgress(progress);
|
||||
Debug.Log($"[SocialRush]Actual progress set to {progress}");
|
||||
}
|
||||
|
||||
static void SocialRushReset()
|
||||
{
|
||||
var arc = GContext.container.Resolve<Assets.Scripts.UI.SocialRush.SocialRushArchitecture>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("SocialRushArchitechture not found");
|
||||
return;
|
||||
}
|
||||
arc.Model.Reset();
|
||||
Debug.Log($"[SocialRush]Reset Done.");
|
||||
}
|
||||
static void SocialRushPeek()
|
||||
{
|
||||
var arc = GContext.container.Resolve<Assets.Scripts.UI.SocialRush.SocialRushArchitecture>();
|
||||
if (arc == null)
|
||||
{
|
||||
Debug.Log("SocialRushArchitechture not found");
|
||||
return;
|
||||
}
|
||||
Debug.Log($"[SocialRush] Display: {arc.Model.ProgressDisplay}, Actual {arc.Model.ProgressActual}, inflation rate: {arc.Model.InflationRate}, event Id: {arc.EventId}");
|
||||
}
|
||||
static void EventMowForTreasureGuideClear()
|
||||
{
|
||||
GContext.container.Resolve<EventMowForTreasure.EventMowForTreasureManager>()?.ClearGuide();
|
||||
}
|
||||
|
||||
static void OfferStoryChainReset()
|
||||
{
|
||||
var manager = GContext.container.Resolve<OfferStoryChain.Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
Debug.Log("OfferStoryChain.Manager not found");
|
||||
return;
|
||||
}
|
||||
manager.Reset();
|
||||
}
|
||||
|
||||
static void OfferStoryChainPeek()
|
||||
{
|
||||
var manager = GContext.container.Resolve<OfferStoryChain.Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
Debug.Log("OfferStoryChain.Manager not found");
|
||||
return;
|
||||
}
|
||||
var str = "EventId: " + manager.EventId + "\n" + "Progress: " + manager.Progress + "\n" + "VipLevel: " + manager.VipLevel + "\n" + "RoundCount: " + manager.RoundCount;
|
||||
Debug.Log("OfferStoryChain.Manager:" + str);
|
||||
}
|
||||
|
||||
static void OfferTreasureAscentPeek()
|
||||
{
|
||||
var manager = GContext.container.Resolve<OfferTreasureAscent.Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
Debug.Log("OfferTreasureAscent.Manager not found");
|
||||
return;
|
||||
}
|
||||
Debug.Log($"[OfferTreasureAscent] eventId: {manager.EventId}, status: {manager.Status}, roundCount: {manager.RoundCount}");
|
||||
}
|
||||
|
||||
static void OfferTreasureAscentReset()
|
||||
{
|
||||
var manager = GContext.container.Resolve<OfferTreasureAscent.Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
Debug.Log("OfferTreasureAscent.Manager not found");
|
||||
return;
|
||||
}
|
||||
manager.Reset();
|
||||
Debug.Log($"[OfferTreasureAscent] Reset Done.");
|
||||
}
|
||||
|
||||
static void PinballUncountableBlockKick(int block)
|
||||
{
|
||||
var manager = GContext.container.Resolve<PinballUncountable.Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
Debug.Log("PinballUncountable.Manager not found");
|
||||
return;
|
||||
}
|
||||
manager.EventAggregator.Publish(new PinballUncountable.EventBlockKick { Block = block != 0 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,829 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PlayFab;
|
||||
using PlayFab.MultiplayerModels;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class EventFishingDuelPanel : BasePanel
|
||||
{
|
||||
Tables tables;
|
||||
Button btn_close;
|
||||
GameObject btn_close_image;
|
||||
Animation anim;
|
||||
|
||||
GameObject MiddlePanel;
|
||||
TMP_Text text_time;
|
||||
Image icon_rank;
|
||||
TMP_Text text_rank;
|
||||
Button btn_questionmark1;
|
||||
Button btn_questionmark;
|
||||
GameObject bg_bar;
|
||||
Image bar;
|
||||
TMP_Text text_progress;
|
||||
Button btn_reward;
|
||||
List<RewardItemNew> rewardItemNews;
|
||||
TMP_Text win_text_num;
|
||||
TMP_Text loss_text_num;
|
||||
TMP_Text middle_text_time;
|
||||
Button icon_ticket;
|
||||
Button btn_confirm;
|
||||
GameObject loading;
|
||||
GameObject p_text;
|
||||
TMP_Text text_num;
|
||||
|
||||
//GameObject effect_beilv_loop;
|
||||
Button btn_beilv;
|
||||
//Image btn_beilv_image;
|
||||
Button btn_beilv_max;
|
||||
//private Image btn_beilv_max_image;
|
||||
|
||||
TMP_Text text_beilv;
|
||||
TMP_Text text_beilv_max;
|
||||
TMP_Text text_beilv_show;
|
||||
|
||||
|
||||
GameObject matchingPanel;
|
||||
DuelMatchPlayer myself;
|
||||
DuelMatchPlayer enemy;
|
||||
GameObject fx_ui_matchingPanel_match_glow_01;
|
||||
TMP_Text match_text_time;
|
||||
Button but_stop;
|
||||
Coroutine matchIng;
|
||||
FishingDuelManager FDM;
|
||||
Sound audio_matchingloop;
|
||||
AudioClip audio_ui_fishingduel_matchingloop;
|
||||
IUIService uiService;
|
||||
Timer timer;
|
||||
FishingEventData fishingEventData;
|
||||
|
||||
Button btn_advert;
|
||||
|
||||
DuelRandomMapPanel randomMapPanel;
|
||||
|
||||
GameObject fishConfrimPanel;
|
||||
|
||||
TicketTips ticket_tips;
|
||||
|
||||
IDisposable disRequestSvrTime;
|
||||
float MatchPlayerProb;
|
||||
private void Awake()
|
||||
{
|
||||
fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
uiService = GContext.container.Resolve<IUIService>();
|
||||
tables = GContext.container.Resolve<Tables>();
|
||||
btn_close = transform.Find("bottom/btn_close").GetComponent<Button>();
|
||||
btn_close_image = transform.Find("bottom/btn_close/Image").gameObject;
|
||||
GContext.container.Unregister<FishingDuelManager>();
|
||||
FDM = new FishingDuelManager();
|
||||
GContext.container.RegisterInstance(FDM);
|
||||
btn_advert = transform.Find("root/MiddlePanel/bottom/btn_advert").GetComponent<Button>();
|
||||
//====================倍率调整
|
||||
btn_beilv = transform.Find("root/MiddlePanel/bottom/beilv").GetComponent<Button>();
|
||||
btn_beilv_max = transform.Find("root/MiddlePanel/bottom/beilv_max").GetComponent<Button>();
|
||||
|
||||
text_beilv = transform.Find("root/MiddlePanel/bottom/beilv/text_beilv").GetComponent<TMP_Text>();
|
||||
text_beilv_max = transform.Find("root/MiddlePanel/bottom/beilv_max/text_beilv").GetComponent<TMP_Text>();
|
||||
|
||||
|
||||
//====================
|
||||
ticket_tips = transform.Find("root/MiddlePanel/bottom/ticket_tips").GetComponent<TicketTips>();
|
||||
//UI_EventPartnerFishbowlPanel_27
|
||||
anim = transform.Find("root/matchingPanel").GetComponent<Animation>();
|
||||
btn_questionmark1 = transform.Find("root/MiddlePanel/top/title/btn_questionmark").GetComponent<Button>();
|
||||
//===================匹配界面
|
||||
MiddlePanel = transform.Find("root/MiddlePanel").gameObject;
|
||||
text_time = transform.Find("root/MiddlePanel/top/text_time").GetComponent<TMP_Text>();
|
||||
icon_rank = transform.Find("root/MiddlePanel/rank/icon_rank").GetComponent<Image>();
|
||||
text_rank = transform.Find("root/MiddlePanel/rank/text_rank").GetComponent<TMP_Text>();
|
||||
btn_questionmark = transform.Find("root/MiddlePanel/rank/text_rank/btn_questionmark").GetComponent<Button>();
|
||||
bg_bar = transform.Find("root/MiddlePanel/rank/bg_bar").gameObject;
|
||||
bar = transform.Find("root/MiddlePanel/rank/bg_bar/bar").GetComponent<Image>();
|
||||
text_progress = transform.Find("root/MiddlePanel/rank/bg_bar/text_progress").GetComponent<TMP_Text>();
|
||||
btn_reward = transform.Find("root/MiddlePanel/rank/btn_reward").GetComponent<Button>();
|
||||
rewardItemNews = new List<RewardItemNew>();
|
||||
Transform layout_reward = transform.Find("root/MiddlePanel/reward/layout_reward");
|
||||
int layout_reward_count = layout_reward.childCount;
|
||||
for (int i = 0; i < layout_reward_count; i++)
|
||||
{
|
||||
rewardItemNews.Add(layout_reward.GetChild(i).GetComponent<RewardItemNew>());
|
||||
}
|
||||
|
||||
icon_ticket = transform.Find("root/MiddlePanel/bottom/icon_ticket").GetComponent<Button>();
|
||||
|
||||
win_text_num = transform.Find("root/MiddlePanel/reward/info/win/text_num").GetComponent<TMP_Text>();
|
||||
loss_text_num = transform.Find("root/MiddlePanel/reward/info/loss/text_num").GetComponent<TMP_Text>();
|
||||
btn_confirm = transform.Find("root/MiddlePanel/bottom/btn_confirm/btn_green").GetComponent<Button>();
|
||||
loading = transform.Find("root/MiddlePanel/bottom/btn_confirm/btn_green/Ani_Container/loading").gameObject;
|
||||
p_text = transform.Find("root/MiddlePanel/bottom/btn_confirm/btn_green/Ani_Container/p_text").gameObject;
|
||||
middle_text_time = transform.Find("root/MiddlePanel/top/text_time").GetComponent<TMP_Text>();
|
||||
text_num = transform.Find("root/MiddlePanel/bottom/icon_ticket/text_num").GetComponent<TMP_Text>();
|
||||
//================================
|
||||
//=====================匹配中
|
||||
matchingPanel = transform.Find("root/matchingPanel").gameObject;
|
||||
myself = transform.Find("root/matchingPanel/myself").GetComponent<DuelMatchPlayer>();
|
||||
enemy = transform.Find("root/matchingPanel/enermy").GetComponent<DuelMatchPlayer>();
|
||||
match_text_time = transform.Find("root/matchingPanel/text_time").GetComponent<TMP_Text>();
|
||||
but_stop = transform.Find("root/matchingPanel/btn_stop/btn_green").GetComponent<Button>();
|
||||
fx_ui_matchingPanel_match_glow_01 = transform.Find("root/matchingPanel/enermy/fx_ui_matchingPanel_match_glow_01").gameObject;
|
||||
//=================================
|
||||
randomMapPanel = transform.Find("root/matchingPanel/RandomMap").GetComponent<DuelRandomMapPanel>();
|
||||
fishConfrimPanel = transform.Find("root/FishConfirmPanel").gameObject;
|
||||
fishConfrimPanel.SetActive(false);
|
||||
LoadAudio();
|
||||
GContext.container.Resolve<GuideDataCenter>().InspectTriggerGuide("EventFishingDuelPanel");
|
||||
CheckResource();
|
||||
MatchPlayerProb = FDM.eventSoloConfig.MatchPlayerProb;
|
||||
}
|
||||
ILoadResourceService loadResourceService;
|
||||
IDisposable loadResourceIdis;
|
||||
string resourceKey;
|
||||
public async void CheckResource()
|
||||
{
|
||||
PlayerFishData playerFishData = GContext.container.Resolve<PlayerFishData>();
|
||||
List<int> rodIDs = tables.TbRodData.DataList.FindAll(_ => !playerFishData.NotRod(_.ID)).Select(_ => _.ID).ToList();
|
||||
|
||||
List<string> resourceName = new List<string>();
|
||||
RodSkinData rodSkinData;
|
||||
foreach (var rodID in rodIDs)
|
||||
{
|
||||
int skindID = playerFishData.GetRodSkin(rodID);
|
||||
rodSkinData = tables.TbRodSkinData.GetOrDefault(skindID);
|
||||
resourceName.Add(rodSkinData.Fbx);
|
||||
}
|
||||
loadResourceService = GContext.container.Resolve<ILoadResourceService>();
|
||||
resourceKey = "RodList" + rodIDs.Count;
|
||||
SetLoading(true);
|
||||
bool isReady = await loadResourceService.CheckResourceLoadQueue(resourceKey, resourceName);
|
||||
if (!isReady)
|
||||
{
|
||||
loadResourceIdis = GContext.OnEvent<LoadEventResourceEvent>().Where(_ => _.key == resourceKey).Subscribe(LoadEventResourceEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadEventResource(true);
|
||||
}
|
||||
}
|
||||
void LoadEventResourceEvent(LoadEventResourceEvent loadEvent)
|
||||
{
|
||||
Debug.Log($"LoadEventResourceEvent {loadEvent.key} {loadEvent.isSucceeded} ");
|
||||
DisposeEvent();
|
||||
LoadEventResource(loadEvent.isSucceeded);
|
||||
}
|
||||
|
||||
void DisposeEvent()
|
||||
{
|
||||
loadResourceIdis?.Dispose();
|
||||
loadResourceIdis = null;
|
||||
}
|
||||
void LoadEventResource(bool succeeded)
|
||||
{
|
||||
if (this == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (succeeded)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetLoading(false);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError($"Load Event Resource Exception! == {gameObject.name} \n {e}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Load Event Resource Failed! == {gameObject.name}");
|
||||
}
|
||||
}
|
||||
async void LoadAudio()
|
||||
{
|
||||
audio_ui_fishingduel_matchingloop = await Addressables.LoadAssetAsync<AudioClip>("audio_ui_fishingduel_matchingloop").Task;
|
||||
}
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
GContext.Publish(new HideHomePanelEvent());
|
||||
//disposable = GContext.OnEvent<RewardPanelClose>().Subscribe(OnRewardPanelClose);
|
||||
ShowPanel(false);
|
||||
btn_close.onClick.AddListener(OnClickClose);
|
||||
|
||||
InitMiddlePanel();
|
||||
|
||||
but_stop.onClick.AddListener(OnClickStop);
|
||||
|
||||
IUserService userService = GContext.container.Resolve<IUserService>();
|
||||
var url = userService.AvatarUrl;
|
||||
|
||||
uiService.SetHeadImage(myself.icon_head, url);
|
||||
myself.text_name.text = userService.DisplayName;
|
||||
myself.icon_rank.sprite = icon_rank.sprite;
|
||||
myself.text_score.text = ConvertTools.GetNumberString2(FDM.duelData.progress);
|
||||
|
||||
btn_beilv.onClick.AddListener(OnclickMagnification);
|
||||
btn_beilv_max.onClick.AddListener(OnclickMagnification);
|
||||
|
||||
SetShowMagnification();
|
||||
btn_reward.onClick.AddListener(() => _ = UIManager.Instance.ShowUI(UITypes.EventFishingDuelRewardPopupPanel));
|
||||
btn_advert.onClick.AddListener(() => _ = UIManager.Instance.ShowUI(UITypes.EventFishingduelShopPopupPanel));
|
||||
Item itemdata = tables.TbItem.GetOrDefault(2051);
|
||||
string nameL = LocalizationMgr.GetText(itemdata.Name_l10n_key);
|
||||
ticket_tips.SetText(LocalizationMgr.GetFormatTextValue("UI_EventPartnerFishbowlPanel_27", nameL));
|
||||
}
|
||||
|
||||
void OnclickMagnification()
|
||||
{
|
||||
FDM.SetMagnification();
|
||||
SetShowMagnification();
|
||||
}
|
||||
void SetShowMagnification()
|
||||
{
|
||||
bool IsMax = FDM.IsMaxMagnification();
|
||||
btn_beilv.gameObject.SetActive(!IsMax);
|
||||
btn_beilv_max.gameObject.SetActive(IsMax);
|
||||
text_beilv.text = text_beilv_max.text = $"x{FDM.magnification}";
|
||||
SetReward();
|
||||
}
|
||||
//void OnRewardPanelClose(RewardPanelClose rewardPanelClose)
|
||||
//{
|
||||
// SetEnergy();
|
||||
//}
|
||||
void SetEnergy()
|
||||
{
|
||||
//获取配置表
|
||||
|
||||
int MatchCost = FDM.magnification;
|
||||
int Energy = FDM.duelData.tickets;
|
||||
if (MatchCost > Energy)
|
||||
{
|
||||
//ConvertTools.GetNumberString
|
||||
text_num.text = $"<color=red>{Energy}/{MatchCost}</color>";
|
||||
}
|
||||
else
|
||||
{
|
||||
text_num.text = $"{Energy}/{MatchCost}";
|
||||
}
|
||||
}
|
||||
void SetReward()
|
||||
{
|
||||
var rewardItemData = FDM.GetReward();
|
||||
if (rewardItemData != null)
|
||||
{
|
||||
int dataCount = rewardItemData.Count;
|
||||
int rewardCount = rewardItemNews.Count;
|
||||
for (int i = 0; i < rewardCount; i++)
|
||||
{
|
||||
if (i < dataCount)
|
||||
{
|
||||
rewardItemData[i].count *= FDM.magnification;
|
||||
rewardItemNews[i].SetData(rewardItemData[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
rewardItemNews[i].gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
win_text_num.text = $"+{FDM.eventSolomain.WinTrophy * FDM.magnification}";
|
||||
loss_text_num.text = $"-{FDM.eventSolomain.LoseTrophy * FDM.magnification}";
|
||||
SetEnergy();
|
||||
}
|
||||
void InitMiddlePanel()
|
||||
{
|
||||
DoConsumeBait();
|
||||
icon_ticket.onClick.AddListener(OnClickIcon);
|
||||
btn_confirm.onClick.AddListener(OnClickMaching);
|
||||
btn_questionmark.onClick.AddListener(() => _ = UIManager.Instance.ShowUI(UITypes.EventFishingDuelRankPopupPanel));
|
||||
btn_questionmark1.onClick.AddListener(() => _ = UIManager.Instance.ShowUI(UITypes.EventFishingDuelInfoPopupPanel));
|
||||
uiService.SetImageSprite(icon_rank, FDM.eventSolomain.RankIcon);
|
||||
text_rank.text = LocalizationMgr.GetText(FDM.eventSolomain.RankText_l10n_key);
|
||||
text_progress.text = $"{FDM.duelData.progress}";
|
||||
win_text_num.text = $"+{FDM.eventSolomain.WinTrophy * FDM.magnification}";
|
||||
loss_text_num.text = $"-{FDM.eventSolomain.LoseTrophy * FDM.magnification}";
|
||||
}
|
||||
private void DoConsumeBait()
|
||||
{
|
||||
Debug.Log("NetworkReachability");
|
||||
if (Application.internetReachability == NetworkReachability.NotReachable)
|
||||
{
|
||||
Debug.Log("NetworkReachability.NotReachable");
|
||||
NoInternet();
|
||||
}
|
||||
}
|
||||
|
||||
private async void NoInternet()
|
||||
{
|
||||
GameObject go = await UIManager.Instance.ShowUI(UITypes.NoticeConfirmPopupPanel);
|
||||
var NoticeConfirmPopupPanel = go.GetComponent<NoticeConfirmPopupPanel>();
|
||||
string info = LocalizationMgr.GetText("UI_FishingReconnectPanel_101003");
|
||||
string label = LocalizationMgr.GetText("UI_FishingRewardPanel_101022");
|
||||
string title = LocalizationMgr.GetText("UI_FishingReconnectPanel_101002");
|
||||
NoticeConfirmPopupPanel.Init(1, title, info,
|
||||
onClickLeft: DoConsumeBait,
|
||||
onClose: DoConsumeBait,
|
||||
btn_left_text: label);
|
||||
}
|
||||
void OnClickMaching()
|
||||
{
|
||||
if (FDM.duelData.tickets < FDM.magnification)
|
||||
{
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_86"));
|
||||
//var pos = btn_confirm.transform.position;
|
||||
//pos.y += 50;
|
||||
//ticket_tips.SetPos(pos);
|
||||
//ticket_tips.gameObject.SetActive(!ticket_tips.gameObject.activeSelf);
|
||||
//门票不足
|
||||
FDM.SetMaxMagnification();
|
||||
SetShowMagnification();
|
||||
return;
|
||||
}
|
||||
if (loading.activeSelf)
|
||||
{
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_114"));
|
||||
return;
|
||||
}
|
||||
disRequestSvrTime?.Dispose();
|
||||
disRequestSvrTime = GContext.OnEvent<RequestSvrTimeEvent>().Subscribe(RequestSvrTimeEvent);
|
||||
ShowPanel(true);
|
||||
matchIng = StartCoroutine(StartMaching());
|
||||
}
|
||||
void SetLoading(bool value)
|
||||
{
|
||||
//loading.SetActive(value);
|
||||
loading.SetActive(value);
|
||||
p_text.SetActive(!value);
|
||||
}
|
||||
void RequestSvrTimeEvent(RequestSvrTimeEvent requestSvrTimeEvent)
|
||||
{
|
||||
if (!requestSvrTimeEvent.Status)
|
||||
{
|
||||
OnClickStop();
|
||||
}
|
||||
}
|
||||
void OnClickIcon()
|
||||
{
|
||||
var pos = icon_ticket.transform.position;
|
||||
pos.y += 40;
|
||||
ticket_tips.SetPos(pos);
|
||||
ticket_tips.gameObject.SetActive(!ticket_tips.gameObject.activeSelf);
|
||||
|
||||
}
|
||||
void OnClickStop()
|
||||
{
|
||||
FDM.StopRT();
|
||||
CancelMatchmaking();
|
||||
disRequestSvrTime?.Dispose();
|
||||
disRequestSvrTime = null;
|
||||
ticketId = false;
|
||||
if (matchIng != null)
|
||||
{
|
||||
StopCoroutine(matchIng);
|
||||
matchIng = null;
|
||||
}
|
||||
randomMapPanel.Stop();
|
||||
ShowPanel(false);
|
||||
}
|
||||
void ShowPanel(bool isMatch)
|
||||
{
|
||||
if (isMatch)
|
||||
{
|
||||
StopTimer();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTimer();
|
||||
}
|
||||
btn_close.enabled = !isMatch;
|
||||
btn_close_image.SetActive(!isMatch);
|
||||
MiddlePanel.SetActive(!isMatch);
|
||||
matchingPanel.SetActive(isMatch);
|
||||
StopLoopAudio();
|
||||
}
|
||||
void PlayLoopAudio()
|
||||
{
|
||||
StopLoopAudio();
|
||||
audio_matchingloop = GContext.container.Resolve<ISoundService>().GetNewUISound(audio_ui_fishingduel_matchingloop);
|
||||
audio_matchingloop.audioSource.Play();
|
||||
audio_matchingloop.audioSource.loop = true;
|
||||
}
|
||||
void StopLoopAudio()
|
||||
{
|
||||
if (audio_matchingloop)
|
||||
{
|
||||
audio_matchingloop.audioSource.Stop();
|
||||
audio_matchingloop.ReturnPool();
|
||||
audio_matchingloop = null;
|
||||
}
|
||||
}
|
||||
IEnumerator StartMaching()
|
||||
{
|
||||
but_stop.gameObject.SetActive(false);
|
||||
//anim.Play("eventfishingduelpanel_maprandom");
|
||||
Dictionary<int, Robot> robots = tables.TbRobot.DataMap;
|
||||
List<int> RobotAccountList = FDM.eventSolomain.RobotAccountList;
|
||||
int robotId = RobotAccountList[UnityEngine.Random.Range(0, RobotAccountList.Count)];
|
||||
EventSoloRobot eventSoloRobot = tables.TbEventSoloRobot.GetOrDefault(robotId);
|
||||
List<int> robotList = eventSoloRobot.RobotList;
|
||||
robotId = robotList[UnityEngine.Random.Range(0, robotList.Count)];
|
||||
Robot robot = robots[robotId];
|
||||
SetRobot(robot, eventSoloRobot.Trophy);
|
||||
//等待匹配
|
||||
randomMapPanel.StartMap();
|
||||
PlayLoopAudio();
|
||||
yield return new WaitForSeconds(1);
|
||||
currentTicketId = null;
|
||||
ticketId = false;
|
||||
yield return FDM.StartMaching().AsIEnumerator();
|
||||
int MaxMatchTime = FDM.eventSoloConfig.MaxMatchTime;
|
||||
float matchPlayerProb = UnityEngine.Random.value;
|
||||
if (matchPlayerProb >= MatchPlayerProb || MaxMatchTime < 2 || FDM.rts == null)
|
||||
{
|
||||
but_stop.gameObject.SetActive(true);
|
||||
ticketId = true;
|
||||
yield return Awaiters.Seconds(UnityEngine.Random.Range(0, MaxMatchTime));
|
||||
if (ticketId)
|
||||
{
|
||||
FDM.MachingEnd(null);
|
||||
}
|
||||
ticketId = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateMatchmakingTicket();
|
||||
}
|
||||
//anim.Play("eventfishingduelpanel_loop");
|
||||
float time = 0.1f;
|
||||
while (FDM.MachingState < 2)
|
||||
{
|
||||
robotId = RobotAccountList[UnityEngine.Random.Range(0, RobotAccountList.Count)];
|
||||
eventSoloRobot = tables.TbEventSoloRobot.GetOrDefault(robotId);
|
||||
|
||||
robotList = eventSoloRobot.RobotList;
|
||||
robotId = robotList[UnityEngine.Random.Range(0, robotList.Count)];
|
||||
|
||||
robot = robots[robotId];
|
||||
SetRobot(robot, eventSoloRobot.Trophy);
|
||||
yield return new WaitForSeconds(time);
|
||||
}
|
||||
|
||||
but_stop.gameObject.SetActive(false);
|
||||
disRequestSvrTime?.Dispose();
|
||||
disRequestSvrTime = null;
|
||||
FDM.OnJoin();
|
||||
var matchDelayTime = FDM.eventSoloConfig.MatchDelayTime;
|
||||
float machTimeM = UnityEngine.Random.Range(matchDelayTime[0], matchDelayTime[1]);
|
||||
//最后两秒等的越来越长
|
||||
while (machTimeM > 0f)
|
||||
{
|
||||
robotId = RobotAccountList[UnityEngine.Random.Range(0, RobotAccountList.Count)];
|
||||
eventSoloRobot = tables.TbEventSoloRobot.GetOrDefault(robotId);
|
||||
|
||||
robotList = eventSoloRobot.RobotList;
|
||||
robotId = robotList[UnityEngine.Random.Range(0, robotList.Count)];
|
||||
|
||||
robot = robots[robotId];
|
||||
SetRobot(robot, eventSoloRobot.Trophy);
|
||||
machTimeM -= time;
|
||||
yield return new WaitForSeconds(time);
|
||||
}
|
||||
randomMapPanel.SetClose();
|
||||
while (!randomMapPanel.IsStop)
|
||||
{
|
||||
robotId = RobotAccountList[UnityEngine.Random.Range(0, RobotAccountList.Count)];
|
||||
eventSoloRobot = tables.TbEventSoloRobot.GetOrDefault(robotId);
|
||||
|
||||
robotList = eventSoloRobot.RobotList;
|
||||
robotId = robotList[UnityEngine.Random.Range(0, robotList.Count)];
|
||||
|
||||
robot = robots[robotId];
|
||||
SetRobot(robot, eventSoloRobot.Trophy);
|
||||
yield return new WaitForSeconds(time);
|
||||
}
|
||||
fx_ui_matchingPanel_match_glow_01.SetActive(true);
|
||||
|
||||
uiService.SetHeadImage(enemy.icon_head, FDM.EnemyModel.PlayerAvatar);
|
||||
enemy.text_name.text = FDM.EnemyModel.PlayerName;
|
||||
var eventSolomain = FDM.GetEventSolomain(FDM.EnemyModel.RankScore);
|
||||
uiService.SetImageSprite(enemy.icon_rank, eventSolomain.RankIcon);
|
||||
enemy.text_score.text = ConvertTools.GetNumberString2(FDM.EnemyModel.RankScore);
|
||||
StopLoopAudio();
|
||||
yield return new WaitForSeconds(2);
|
||||
anim.Play("eventfishingduelpanel_matchingpanel_end");
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
//PlayLoopAudio();
|
||||
//yield return new WaitForSeconds(2);
|
||||
//StopLoopAudio();
|
||||
ShowFishConfrimPanel();
|
||||
}
|
||||
void SetRobot(Robot robot, int trophy)
|
||||
{
|
||||
int robotTier = FDM.eventSolomain.MatchTierList[UnityEngine.Random.Range(0, FDM.eventSolomain.MatchTierList.Count)];
|
||||
var eventSolomain = tables.TbEventSolomain.DataMap[robotTier];
|
||||
uiService.SetHeadImage(enemy.icon_head, robot.Avatar);
|
||||
enemy.text_name.text = LocalizationMgr.GetText(robot.Name_l10n_key);
|
||||
uiService.SetImageSprite(enemy.icon_rank, eventSolomain.RankIcon);
|
||||
enemy.text_score.text = ConvertTools.GetNumberString2(trophy);
|
||||
}
|
||||
public void OnClickClose()
|
||||
{
|
||||
GContext.container.Unregister<FishingDuelManager>();
|
||||
GContext.Publish(new RestartShowHomeUIEvent());
|
||||
UIManager.Instance.DestroyUI(UITypes.EventFishingDuelPanel);
|
||||
disRequestSvrTime?.Dispose();
|
||||
disRequestSvrTime = null;
|
||||
}
|
||||
|
||||
void SetTimer()
|
||||
{
|
||||
StopTimer();
|
||||
DateTime endDateTime = fishingEventData.GetEventEndTime(fishingEventData.duelData.eventID)
|
||||
.AddSeconds(-tables.TbEventSoloConfig.SettlementTime);
|
||||
TimeSpan now = endDateTime - ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
text_time.text = LocalizationMgr.GetFormatTextValue("UI_COMMON_reset",
|
||||
ConvertTools.ConvertTime2(now.Days, now.Hours, now.Minutes, now.Seconds));
|
||||
double seconds = now.TotalSeconds;
|
||||
if (seconds < 1)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
timer = this.AttachTimer((float)seconds, Refresh,
|
||||
(elapsed) =>
|
||||
{
|
||||
now = endDateTime - ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
text_time.text = LocalizationMgr.GetFormatTextValue("UI_COMMON_reset",
|
||||
ConvertTools.ConvertTime2(now.Days, now.Hours, now.Minutes, now.Seconds));
|
||||
}, useRealTime: true);
|
||||
}
|
||||
}
|
||||
void StopTimer()
|
||||
{
|
||||
timer?.Cancel();
|
||||
timer = null;
|
||||
}
|
||||
void Refresh()
|
||||
{
|
||||
timer?.Cancel();
|
||||
timer = null;
|
||||
OnClickClose();
|
||||
UIManager.Instance.DestroyUI(UITypes.EventFishingDuelRankPopupPanel);
|
||||
UIManager.Instance.DestroyUI(UITypes.EventFishingDuelRewardPopupPanel);
|
||||
UIManager.Instance.DestroyUI(UITypes.EventFishingduelShopPopupPanel);
|
||||
UIManager.Instance.DestroyUI(UITypes.EventFishingDuelInfoPopupPanel);
|
||||
}
|
||||
|
||||
public void ShowFishConfrimPanel()
|
||||
{
|
||||
matchingPanel.gameObject.SetActive(false);
|
||||
fishConfrimPanel.SetActive(true);
|
||||
}
|
||||
|
||||
#region Matchmaking
|
||||
public string currentTicketId;
|
||||
public bool ticketId;
|
||||
string GetDetails()
|
||||
{
|
||||
IUserService userService = GContext.container.Resolve<IUserService>();
|
||||
MatchDetails matchDetails = new MatchDetails();
|
||||
matchDetails.ID = userService.UserId;
|
||||
matchDetails.Name = userService.DisplayName;
|
||||
matchDetails.Avatar = userService.AvatarUrl;
|
||||
matchDetails.RandScore = FDM.duelData.progress;
|
||||
matchDetails.Mag = FDM.magnification;
|
||||
return Newtonsoft.Json.JsonConvert.SerializeObject(matchDetails);
|
||||
}
|
||||
void CreateMatchmakingTicket()
|
||||
{
|
||||
if (GContext.container.Resolve<IUserService>() is not PlayFabUserService)
|
||||
{
|
||||
Debug.LogWarning("KV matchmaking service is not connected.");
|
||||
FDM.MachingEnd(null);
|
||||
return;
|
||||
}
|
||||
int MaxMatchTime = FDM.eventSoloConfig.MaxMatchTime;
|
||||
//可匹配段位列表,杯数,选择倍率,地图列表,随机种子seed,鱼卡等级
|
||||
List<int> matchTierList = FDM.eventSolomain.MatchTierList;
|
||||
List<int> mapIDs = FDM.mapIDs;
|
||||
|
||||
List<string> matchTierListStr = new List<string>();
|
||||
for (int i = 0; i < matchTierList.Count; i++)
|
||||
{
|
||||
matchTierListStr.Add(matchTierList[i].ToString());
|
||||
}
|
||||
List<string> mapIDsStr = new List<string>();
|
||||
for (int i = 0; i < mapIDs.Count; i++)
|
||||
{
|
||||
mapIDsStr.Add(mapIDs[i].ToString());
|
||||
}
|
||||
IUserService userService = GContext.container.Resolve<IUserService>();
|
||||
List<string> Group = FDM.GetGroups();
|
||||
string details = GetDetails();
|
||||
var request = new CreateMatchmakingTicketRequest
|
||||
{
|
||||
Creator = new MatchmakingPlayer
|
||||
{
|
||||
Entity = new EntityKey
|
||||
{
|
||||
Id = ((PlayFabUserService)userService).entityKey.Id,
|
||||
Type = ((PlayFabUserService)userService).entityKey.Type
|
||||
},
|
||||
Attributes = new MatchmakingPlayerAttributes
|
||||
{
|
||||
DataObject = new
|
||||
{
|
||||
Tier = matchTierListStr.ToArray(),
|
||||
Map = mapIDsStr.ToArray(),
|
||||
Group = Group.ToArray(),
|
||||
Details = details,
|
||||
}
|
||||
}
|
||||
},
|
||||
GiveUpAfterSeconds = MaxMatchTime,
|
||||
QueueName = FishingDuelManager.QueueName // 需要根据实际的匹配队列名称修改
|
||||
};
|
||||
|
||||
PlayFabMultiplayerAPI.CreateMatchmakingTicket(request,
|
||||
(result) =>
|
||||
{
|
||||
currentTicketId = result.TicketId;
|
||||
Debug.Log("匹配请求已创建,等待匹配...");
|
||||
Debug.Log("匹配Ticket ID: " + result.TicketId);
|
||||
//// 开始轮询匹配状态
|
||||
StartCoroutine(CheckMatchmakingStatus());
|
||||
//显示取消按钮
|
||||
but_stop.gameObject.SetActive(true);
|
||||
},
|
||||
(error) =>
|
||||
{
|
||||
Debug.LogError("创建匹配请求失败: " + error.GenerateErrorReport());
|
||||
FDM.MachingEnd(null);
|
||||
});
|
||||
}
|
||||
|
||||
IEnumerator CheckMatchmakingStatus()
|
||||
{
|
||||
while (!string.IsNullOrEmpty(currentTicketId))
|
||||
{
|
||||
yield return new WaitForSeconds(3f);
|
||||
|
||||
if (string.IsNullOrEmpty(currentTicketId))
|
||||
yield break;
|
||||
|
||||
var request = new GetMatchmakingTicketRequest
|
||||
{
|
||||
TicketId = currentTicketId,
|
||||
QueueName = FishingDuelManager.QueueName, // 需要根据实际的匹配队列名称修改
|
||||
EscapeObject = false
|
||||
};
|
||||
|
||||
PlayFabMultiplayerAPI.GetMatchmakingTicket(request,
|
||||
(result) =>
|
||||
{
|
||||
string statusInfo = "匹配状态: " + result.Status;
|
||||
if (result.MatchId != null)
|
||||
{
|
||||
statusInfo += ", Match ID: " + result.MatchId;
|
||||
}
|
||||
Debug.Log(statusInfo.ToString());
|
||||
|
||||
if (result.Status == "Matched")
|
||||
{
|
||||
but_stop.gameObject.SetActive(false);
|
||||
Debug.Log("匹配成功,获取服务器信息: ");
|
||||
GetMatchDetails(result.MatchId);
|
||||
}
|
||||
else if (result.Status == "Canceled")
|
||||
{
|
||||
Debug.Log("匹配已取消: Canceled " + result.CancellationReasonString);
|
||||
//if (result.CancellationReasonString != "Requested")
|
||||
//{
|
||||
MachingEnd();
|
||||
//}
|
||||
}
|
||||
},
|
||||
(error) =>
|
||||
{
|
||||
Debug.LogError("检查匹配状态失败: " + error.GenerateErrorReport());
|
||||
MachingEnd();
|
||||
});
|
||||
}
|
||||
}
|
||||
void GetMatchDetails(string matchId)
|
||||
{
|
||||
if (GContext.container.Resolve<IUserService>() is not PlayFabUserService)
|
||||
{
|
||||
Debug.LogWarning("KV matchmaking service is not connected.");
|
||||
FDM.MachingEnd(null);
|
||||
return;
|
||||
}
|
||||
var request = new PlayFab.MultiplayerModels.GetMatchRequest
|
||||
{
|
||||
MatchId = matchId,
|
||||
QueueName = FishingDuelManager.QueueName, // 需要根据实际的匹配队列名称修改
|
||||
EscapeObject = false,
|
||||
ReturnMemberAttributes = true
|
||||
};
|
||||
|
||||
PlayFabMultiplayerAPI.GetMatch(request,
|
||||
(result) =>
|
||||
{
|
||||
Debug.Log("匹配成功: " + result.MatchId);
|
||||
if (string.IsNullOrEmpty(currentTicketId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 添加玩家信息
|
||||
if (result.Members != null && result.Members.Count > 0)
|
||||
{
|
||||
//AddMatchmakingLog("玩家数量: " + result.Members.Count);
|
||||
IUserService userService = GContext.container.Resolve<IUserService>();
|
||||
MatchDetails matchDetails = null;
|
||||
int[] mapArray = null;
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < result.Members.Count; i++)
|
||||
{
|
||||
MatchmakingPlayerWithTeamAssignment member = result.Members[i];
|
||||
if (member.Entity.Id != ((PlayFabUserService)userService).entityKey.Id)
|
||||
{
|
||||
JObject dataObject = JObject.FromObject(result.Members[i].Attributes.DataObject);
|
||||
string[] tierArray = dataObject["Tier"].ToObject<string[]>();
|
||||
mapArray = dataObject["Map"].ToObject<int[]>();
|
||||
string matchDetailsStr = dataObject["Details"].ToString();
|
||||
matchDetails = Newtonsoft.Json.JsonConvert.DeserializeObject<MatchDetails>(matchDetailsStr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
matchDetails = null;
|
||||
mapArray = null;
|
||||
}
|
||||
currentTicketId = null;
|
||||
FDM.MachingEnd(result.MatchId, matchDetails, mapArray);
|
||||
StartCoroutine(CheckReady());
|
||||
}
|
||||
},
|
||||
(error) =>
|
||||
{
|
||||
Debug.LogError("获取匹配详情失败: " + error.GenerateErrorReport());
|
||||
MachingEnd();
|
||||
});
|
||||
}
|
||||
IEnumerator CheckReady()
|
||||
{
|
||||
yield return new WaitForSeconds(5f);
|
||||
if (FDM.MachingState < 2)
|
||||
{
|
||||
Debug.LogError("匹配Ready超时 ");
|
||||
//返回 还是给个机器人? OnClickStop();
|
||||
FDM.MachingState = 0;
|
||||
FDM.MachingEnd(null);
|
||||
}
|
||||
}
|
||||
void MachingEnd()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(currentTicketId))
|
||||
{
|
||||
currentTicketId = null;
|
||||
FDM.MachingEnd(null);
|
||||
}
|
||||
}
|
||||
void CancelMatchmaking()
|
||||
{
|
||||
if (string.IsNullOrEmpty(currentTicketId))
|
||||
return;
|
||||
but_stop.gameObject.SetActive(false);
|
||||
var request = new CancelMatchmakingTicketRequest
|
||||
{
|
||||
TicketId = currentTicketId,
|
||||
QueueName = FishingDuelManager.QueueName // 需要根据实际的匹配队列名称修改
|
||||
};
|
||||
currentTicketId = null;
|
||||
PlayFabMultiplayerAPI.CancelMatchmakingTicket(request,
|
||||
(result) =>
|
||||
{
|
||||
Debug.Log("匹配已取消: ");
|
||||
},
|
||||
(error) =>
|
||||
{
|
||||
Debug.LogError("取消匹配失败: " + error.GenerateErrorReport());
|
||||
});
|
||||
}
|
||||
#endregion Matchmaking
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public abstract class AEventDataManager<T>:IDisposable where T : class, IEventData, new()
|
||||
{
|
||||
protected AEventDataManager()
|
||||
{
|
||||
Disposables=new CompositeDisposable();
|
||||
GContext.OnEvent<TargetEvent>().Subscribe(OnEvent_TargetEvent).AddTo(Disposables);
|
||||
}
|
||||
private Tables _tables;
|
||||
[Inject] public PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
bool disposed;
|
||||
|
||||
[Inject]
|
||||
public Tables Tables
|
||||
{
|
||||
get=>_tables??=GContext.container.Resolve<Tables>();
|
||||
set=>_tables=value;
|
||||
}
|
||||
|
||||
protected CompositeDisposable Disposables { get; }
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
disposed = true;
|
||||
Disposables?.Dispose();
|
||||
}
|
||||
|
||||
#region event
|
||||
|
||||
protected abstract int EventType { get; }
|
||||
protected abstract int EventSubType { get; }
|
||||
|
||||
protected virtual void OnEvent_TargetEvent(TargetEvent targetEvent)
|
||||
{
|
||||
if (disposed || SaveStore == null || !SaveStore.IsCurrent) return;
|
||||
if (targetEvent.type == EventType && targetEvent.subType == EventSubType) RefreshData(targetEvent.id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region data
|
||||
|
||||
protected T _data;
|
||||
|
||||
protected internal T Data => _data ??= new T();
|
||||
|
||||
protected virtual void SaveData()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd {typeof(T).Name} SaveData {JsonConvert.SerializeObject(Data)}");
|
||||
#endif
|
||||
if (disposed) throw new ObjectDisposedException(GetType().Name);
|
||||
SaveStore.Write(typeof(T).Name, JsonConvert.SerializeObject(Data)).RequireAccepted();
|
||||
}
|
||||
|
||||
protected virtual void SaveData(T data)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd {typeof(T).Name} SaveData {JsonConvert.SerializeObject(data)}");
|
||||
#endif
|
||||
if (disposed) throw new ObjectDisposedException(GetType().Name);
|
||||
SaveStore.Write(typeof(T).Name, JsonConvert.SerializeObject(data)).RequireAccepted();
|
||||
}
|
||||
|
||||
protected virtual void RefreshData(int eventId)
|
||||
{
|
||||
//判断周开启时间
|
||||
var fishingEvent = Tables.TbFishingEvent.GetOrDefault(eventId);
|
||||
if (fishingEvent == null) return;
|
||||
var cacheData = GetCacheData();
|
||||
if (cacheData != null && cacheData.EventID == eventId)
|
||||
{
|
||||
Data.SetData(cacheData);
|
||||
return;
|
||||
}
|
||||
|
||||
InitData(eventId);
|
||||
}
|
||||
|
||||
protected T GetCacheData()
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(GetType().Name);
|
||||
var read = SaveStore.Read(typeof(T).Name);
|
||||
var dataStr = read.ValueOrThrow();
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd {typeof(T).Name} LoadData sData {dataStr}");
|
||||
#endif
|
||||
if (read.Found == false) return new T();
|
||||
if (string.IsNullOrWhiteSpace(dataStr)) throw new JsonSerializationException("Stored event data cannot be empty.");
|
||||
return JsonConvert.DeserializeObject<T>(dataStr) ?? throw new JsonSerializationException("Stored event data cannot be null.");
|
||||
}
|
||||
|
||||
protected virtual void InitData(int eventId)
|
||||
{
|
||||
_data = new T();
|
||||
Data.EventID = eventId;
|
||||
Data.InflationRate=GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static void Register<M>()where M:AEventDataManager<T>
|
||||
{
|
||||
var instance=GContext.container.Resolve<M>();
|
||||
if (instance != null) return;
|
||||
GContext.container.Register<M>().AsSingleton();
|
||||
GContext.container.Resolve<M>();
|
||||
}
|
||||
#region Token
|
||||
|
||||
public virtual void AddToken(int count)
|
||||
{
|
||||
var currentCount = GetTokenCount();
|
||||
if (Data == null) return;
|
||||
var totalCount = currentCount + count;
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(Data.EventID, totalCount);
|
||||
}
|
||||
|
||||
protected virtual void RemoveToken(int count)
|
||||
{
|
||||
if (Data == null) return;
|
||||
var eventData = GContext.container.Resolve<FishingEventData>();
|
||||
if (eventData == null) return;
|
||||
var current = GetTokenCount();
|
||||
var resCount = Mathf.Max(0, current - count);
|
||||
eventData.SaveTransitionData(Data.EventID, resCount);
|
||||
}
|
||||
|
||||
public virtual int GetTokenCount()
|
||||
{
|
||||
if (Data == null) return 0;
|
||||
var eventData = GContext.container.Resolve<FishingEventData>();
|
||||
return eventData?.GetTransitionDataCount(Data.EventID) ?? 0;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,567 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GameCore;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using asap.core;
|
||||
using game;
|
||||
using cfg;
|
||||
|
||||
public class EventBingoModel
|
||||
{
|
||||
public readonly static int BoardSize = 5;
|
||||
public readonly static int NumberCount = BoardSize * BoardSize;
|
||||
public readonly static string SfxItemIn = "audio_ui_eventbingobaseball_in", SfxItemOut = "audio_ui_eventbingobaseball_out";
|
||||
public const int RecordLength = 4;
|
||||
public const char Splitter = '|', Comma = ',';
|
||||
private int _stepInRound, _ticketCount, _roundCount, _eventId, _chainProgress, _chainTaskTokenProgress;
|
||||
float _inflationRate;
|
||||
private EventBingoRoundData _roundData;
|
||||
private EventBingoTableContext _ctx;
|
||||
private bool _isFirstTime;
|
||||
// This is actually used as a dequeue.
|
||||
public LinkedList<EventBingoNumberRollerItemData> NumberRollerDataList;
|
||||
public int[] BoardNumbers => _roundData.BoardNumbers;
|
||||
public EventBingoStepState StepState => _roundData[_stepInRound];
|
||||
public int BoardState => StepState.BoardState;
|
||||
public int TicketCount => _ticketCount;
|
||||
public int RoundCount => _roundCount;
|
||||
public int EventId => _eventId;
|
||||
public float InflationRate => _inflationRate;
|
||||
public int StepInRound => _stepInRound;
|
||||
public bool IsActivated
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ctx == null)
|
||||
return false;
|
||||
var timeLimit = _ctx.GetTimeLimit();
|
||||
return timeLimit.Item1 <= ZZTimeHelper.UtcNow() && ZZTimeHelper.UtcNow() < timeLimit.Item2;
|
||||
}
|
||||
}
|
||||
public bool IsFirstTime => _isFirstTime;
|
||||
public EventBingoTableContext Ctx => _ctx;
|
||||
public int PackId => _ctx.GetPackId();
|
||||
private const string BingoBattlePassKey = "MiniBPBingo";
|
||||
|
||||
public void Init(FishingEvent e)
|
||||
{
|
||||
_eventId = e.ID;
|
||||
_inflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
RefreshTableContext();
|
||||
_roundCount = 0;
|
||||
var seed = _ctx.PickSeed(_roundCount);
|
||||
_roundData = new EventBingoRoundData(seed);
|
||||
_stepInRound = 0;
|
||||
NumberRollerDataList = new LinkedList<EventBingoNumberRollerItemData>();
|
||||
_ticketCount = 0;
|
||||
AddTicket(_ctx.GetWelcomeGift());
|
||||
SetUiPanel(_ctx.GetUiData());
|
||||
_isFirstTime = true;
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public void Load(EventBingoPlayfabData pfData, EventBingoPlayerPreferenceData ppData, FishingEvent e)
|
||||
{
|
||||
_eventId = pfData.EventId;
|
||||
_inflationRate = pfData.InflationRate;
|
||||
RefreshTableContext();
|
||||
_ticketCount = pfData.TicketCount;
|
||||
_roundCount = pfData.RoundCount;
|
||||
_stepInRound = pfData.StepInRound;
|
||||
_roundData = new EventBingoRoundData(pfData.Seed);
|
||||
if (!_roundData.ValidateStepIndex(_stepInRound))
|
||||
{
|
||||
Debug.LogWarning($"[EventBingo] Wrong data detected. Step count out of range. Reset round data.");
|
||||
_stepInRound = 0;
|
||||
var seed = _ctx.PickSeed(_roundCount);
|
||||
_roundData = new EventBingoRoundData(seed);
|
||||
}
|
||||
if (ppData == null || ppData.RollerDataList == null)
|
||||
NumberRollerDataList = new LinkedList<EventBingoNumberRollerItemData>();
|
||||
else
|
||||
NumberRollerDataList = new LinkedList<EventBingoNumberRollerItemData>(ppData.RollerDataList);
|
||||
SetUiPanel(_ctx.GetUiData());
|
||||
_isFirstTime = ppData == null ? true : ppData.IsFirstTime;
|
||||
}
|
||||
|
||||
public void InitTest()
|
||||
{
|
||||
Debug.Log("[EventBingo] Abandon.");
|
||||
}
|
||||
|
||||
public void NextStep()
|
||||
{
|
||||
_stepInRound++;
|
||||
ToPlayfabData().Save();
|
||||
// Debug.Log($"[EventBingo] Step{_stepInRound}: {StepState.Number}.");
|
||||
}
|
||||
|
||||
public void NextRound()
|
||||
{
|
||||
_roundCount++;
|
||||
var seed = _ctx.PickSeed(_roundCount);
|
||||
_roundData = new EventBingoRoundData(seed);
|
||||
_stepInRound = 0;
|
||||
NumberRollerDataList.Clear();
|
||||
ToPlayfabData().Save();
|
||||
ToPlayerPreferenceData().Save();
|
||||
}
|
||||
|
||||
public bool CheckIfTaken(int index)
|
||||
{
|
||||
var flag = 1 << index;
|
||||
return (BoardState & flag) != 0;
|
||||
}
|
||||
|
||||
public List<EventBingoBlockViewData> GetBoardInitiationData()
|
||||
{
|
||||
var res = new List<EventBingoBlockViewData>();
|
||||
for (int i = 0; i < NumberCount; i++)
|
||||
{
|
||||
_roundData.MinorRewards.TryGetValue(i, out var minorReward);
|
||||
res.Add(new EventBingoBlockViewData
|
||||
{
|
||||
number = _roundData.BoardNumbers[i],
|
||||
reward = minorReward,
|
||||
haveItem = CheckIfTaken(i)
|
||||
});
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void AddNumberRecord(int number)
|
||||
{
|
||||
_ctx.GetRandomItemIcon(out int iconIdx, out string iconUrl);
|
||||
NumberRollerDataList.AddLast(new EventBingoNumberRollerItemData
|
||||
{
|
||||
number = number,
|
||||
iconIdx = iconIdx,
|
||||
});
|
||||
while (NumberRollerDataList.Count > RecordLength)
|
||||
NumberRollerDataList.RemoveFirst();
|
||||
ToPlayerPreferenceData().Save();
|
||||
}
|
||||
|
||||
public bool AddTicket(int count)
|
||||
{
|
||||
_ticketCount += count;
|
||||
if (_ticketCount < 0)
|
||||
{
|
||||
_ticketCount -= count;
|
||||
return false;
|
||||
}
|
||||
EventBingoAct.EventAggregator.Publish(new EventBingoTicketUpdateEvent());
|
||||
ToPlayfabData().Save();
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(_eventId, _ticketCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DeliverMinorReward(int index, out ItemData reward)
|
||||
{
|
||||
bool haveReward = _roundData.MinorRewards.TryGetValue(index, out reward);
|
||||
if (haveReward)
|
||||
{
|
||||
reward.changeSource = new ChangeSource(EventId, "Event_Minigame1", "Event_Minigame1_Bingo", "DirectReward");
|
||||
EventBingoSystem.GrantReward(reward);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Debug.Log($"[EventBingo] No reward for idx {index}");
|
||||
}
|
||||
return haveReward;
|
||||
}
|
||||
|
||||
public void RefreshTableContext()
|
||||
{
|
||||
_ctx = new EventBingoTableContext(_eventId);
|
||||
}
|
||||
|
||||
public EventBingoPlayfabData ToPlayfabData()
|
||||
{
|
||||
return new EventBingoPlayfabData(
|
||||
_eventId, _ticketCount, _roundCount, _stepInRound, _roundData.Seed, _chainProgress,
|
||||
_chainTaskTokenProgress, _inflationRate);
|
||||
}
|
||||
|
||||
public EventBingoEntranceData ToEntranceData()
|
||||
{
|
||||
(var startTime, var expiryTime) = _ctx.GetTimeLimit();
|
||||
var miniBattlePassRedPointStatus = RedPointManager.Instance.GetRedPointState(BingoBattlePassKey);
|
||||
var miniBattlePassRedPointIndex = miniBattlePassRedPointStatus ? RedPointManager.Instance.GetRedPointCount(BingoBattlePassKey) : 0;
|
||||
return new EventBingoEntranceData
|
||||
{
|
||||
NeedRedPoint = _ticketCount > 0 || miniBattlePassRedPointStatus,
|
||||
RedPointIndex = _ticketCount + miniBattlePassRedPointIndex,
|
||||
ExpiryTime = expiryTime,
|
||||
StartTime = startTime,
|
||||
Icon = _ctx.GetEntranceIcon(),
|
||||
};
|
||||
}
|
||||
|
||||
public EventBingoPlayerPreferenceData ToPlayerPreferenceData()
|
||||
{
|
||||
return new EventBingoPlayerPreferenceData
|
||||
{
|
||||
RollerDataList = NumberRollerDataList.ToList(),
|
||||
IsFirstTime = _isFirstTime
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#region ChainPack
|
||||
public int GetChainProgress()
|
||||
{
|
||||
return _chainProgress;
|
||||
}
|
||||
|
||||
public void SetChainProgress(int p)
|
||||
{
|
||||
_chainProgress = p;
|
||||
}
|
||||
|
||||
public int GetTaskProgress()
|
||||
{
|
||||
return _chainTaskTokenProgress;
|
||||
}
|
||||
|
||||
public void SetTaskProgress(int p)
|
||||
{
|
||||
_chainTaskTokenProgress = p;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void SetUiPanel(EventBingoUiData panelData)
|
||||
{
|
||||
UITypes.EventBingoInfoPanel.SetType(panelData.InfoPanel);
|
||||
UITypes.EventBingoNeoNormalPackPanel.SetType(panelData.NeoNormalPackPanel);
|
||||
}
|
||||
|
||||
public bool IsHit(int number)
|
||||
{
|
||||
foreach (var n in BoardNumbers)
|
||||
{
|
||||
if (n == number)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetFirstTimeFlag()
|
||||
{
|
||||
_isFirstTime = false;
|
||||
ToPlayerPreferenceData().Save();
|
||||
}
|
||||
|
||||
public void ActivateInstantBingo()
|
||||
{
|
||||
_stepInRound = _roundData.StepCount - 2;
|
||||
}
|
||||
|
||||
public EventNeoNormalPackData ToNeoNormalPackData()
|
||||
{
|
||||
return new EventNeoNormalPackData
|
||||
{
|
||||
PackId = PackId,
|
||||
EventId = EventId,
|
||||
Panel = _ctx.GetPackPanel()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class EventBingoRoundData
|
||||
{
|
||||
private int _seed;
|
||||
public int[] BoardNumbers;
|
||||
private List<int> _playerNumberSequence;
|
||||
private List<int> _stateSequence;
|
||||
private List<int> _playerIdxSequence;
|
||||
public Dictionary<int, ItemData> MinorRewards;
|
||||
public int Seed => _seed;
|
||||
public int StepCount => _stateSequence.Count;
|
||||
|
||||
public EventBingoStepState this[int idx]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (idx >= _stateSequence.Count || idx >= _playerNumberSequence.Count)
|
||||
{
|
||||
var actualIdx = Mathf.Min(_stateSequence.Count - 2, _playerNumberSequence.Count - 2);
|
||||
return new EventBingoStepState
|
||||
{
|
||||
Number = _playerNumberSequence[actualIdx],
|
||||
BoardState = _stateSequence[actualIdx],
|
||||
Index = _playerIdxSequence[actualIdx],
|
||||
};
|
||||
throw new IndexOutOfRangeException(
|
||||
$"[EventBingo] Index {idx} out of range. Expected range: [0, {Math.Min(_stateSequence.Count, _playerNumberSequence.Count)}]");
|
||||
}
|
||||
return new EventBingoStepState
|
||||
{
|
||||
Number = _playerNumberSequence[idx],
|
||||
BoardState = _stateSequence[idx],
|
||||
Index = _playerIdxSequence[idx]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public EventBingoRoundData(int seed)
|
||||
{
|
||||
_playerNumberSequence = new List<int>();
|
||||
_playerIdxSequence = new List<int>();
|
||||
_stateSequence = new List<int>();
|
||||
UpdateWithSeed(seed);
|
||||
}
|
||||
|
||||
public void UpdateWithSeed(int seed)
|
||||
{
|
||||
var model = GContext.container.Resolve<EventBingoModel>();
|
||||
_seed = seed;
|
||||
var rng = new System.Random(_seed);
|
||||
BoardNumbers = EventBingoSystem.GenerateBoardNumbers(rng).ToArray();
|
||||
_playerNumberSequence.Clear();
|
||||
_playerIdxSequence.Clear();
|
||||
_stateSequence.Clear();
|
||||
_playerNumberSequence.Add(-1);
|
||||
_playerIdxSequence.Add(-1);
|
||||
_stateSequence.Add(0b0);
|
||||
var numberPool = Enumerable.Range(1, 99).ToList();
|
||||
int state = 0b0;
|
||||
var pickedIndexSet = new HashSet<int>();
|
||||
while (numberPool.Count > 0 && EventBingoSystem.CheckBingo(state, out _) <= 0)
|
||||
{
|
||||
var pickedNumber = EventBingoSystem.PickRandomNumberFromList(numberPool, rng);
|
||||
var pickedIdx = Array.IndexOf(BoardNumbers, pickedNumber);
|
||||
if (pickedIdx > -1)
|
||||
{
|
||||
pickedIndexSet.Add(pickedIdx);
|
||||
state |= 1 << pickedIdx;
|
||||
}
|
||||
_playerIdxSequence.Add(pickedIdx);
|
||||
_playerNumberSequence.Add(pickedNumber);
|
||||
_stateSequence.Add(state);
|
||||
}
|
||||
MinorRewards = new Dictionary<int, ItemData>();
|
||||
model.Ctx.GetMinorDropData(rng, out var availableRewards, out var displayRewards);
|
||||
|
||||
var allIndices = Enumerable.Range(0, EventBingoModel.NumberCount).ToList();
|
||||
FtUtils.ShuffleList(allIndices, rng);
|
||||
var pickedIndices = pickedIndexSet.ToList();
|
||||
FtUtils.ShuffleList(pickedIndices, rng);
|
||||
var unpickedIndices = allIndices.Except(pickedIndexSet).ToList();
|
||||
FtUtils.ShuffleList(unpickedIndices, rng);
|
||||
// Assign rewards to picked positions
|
||||
for (int i = 0; i < availableRewards.Count && i < pickedIndices.Count; i++)
|
||||
{
|
||||
MinorRewards[pickedIndices[i]] = availableRewards[i];
|
||||
// Debug.Log($"[EventBingo] Available MiniDrop: item {availableRewards[i].id} @ idx{pickedIndices[i]}");
|
||||
}
|
||||
// Assign display rewards to unpicked positions
|
||||
for (int i = 0; i < displayRewards.Count && i < unpickedIndices.Count; i++)
|
||||
{
|
||||
MinorRewards[unpickedIndices[i]] = displayRewards[i];
|
||||
// Debug.Log($"[EventBingo] Unavailable MiniDrop: item {displayRewards[i].id} @ idx {unpickedIndices[i]}");
|
||||
}
|
||||
// var leftOverIndices = Enumerable.Range(0, EventBingoModel.NumberCount)
|
||||
// .Where(x => !pickedIndexSet.Contains(x))
|
||||
// .ToHashSet()
|
||||
// .ToList()
|
||||
// .OrderBy(x => rng.Next())
|
||||
// .ToList();
|
||||
// var pickedIndexList = pickedIndexSet.ToList().OrderBy(x => rng.Next()).ToList();
|
||||
// for (int i = 0; i < availableRewards.Count && i < pickedIndexList.Count; i++)
|
||||
// {
|
||||
// var idx = pickedIndexList.First();
|
||||
// MinorRewards[idx] = availableRewards[i];
|
||||
// pickedIndexList.Remove(idx);
|
||||
// }
|
||||
// for (int i = 0; i < displayRewards.Count && i < leftOverIndices.Count; i++)
|
||||
// {
|
||||
// var idx = leftOverIndices.First();
|
||||
// MinorRewards[idx] = displayRewards[i];
|
||||
// leftOverIndices.Remove(idx);
|
||||
// }
|
||||
}
|
||||
|
||||
public int[] GetNumberRollerDataList(int step)
|
||||
{
|
||||
var startIdx = Mathf.Max(1, step - EventBingoModel.RecordLength + 1);
|
||||
var count = Mathf.Min(EventBingoModel.RecordLength, step);
|
||||
return _playerNumberSequence.GetRange(startIdx, count).ToArray();
|
||||
}
|
||||
|
||||
public bool ValidateStepIndex(int idx)
|
||||
{
|
||||
return idx < _playerNumberSequence.Count && idx < _playerIdxSequence.Count && idx < _stateSequence.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventBingoStepState
|
||||
{
|
||||
public int BoardState;
|
||||
public int Number;
|
||||
public int Index;
|
||||
}
|
||||
|
||||
public class EventBingoPlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int EventId, TicketCount, RoundCount, StepInRound, Seed, ChainProgress, ChainTaskTokenProgress;
|
||||
public float InflationRate;
|
||||
private const int TokenCount = 8;
|
||||
private const string Key = "EventBingo";
|
||||
private string Serialize()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append(EventId).Append(EventBingoModel.Splitter);
|
||||
sb.Append(TicketCount).Append(EventBingoModel.Splitter);
|
||||
sb.Append(RoundCount).Append(EventBingoModel.Splitter);
|
||||
sb.Append(StepInRound).Append(EventBingoModel.Splitter);
|
||||
sb.Append(Seed).Append(EventBingoModel.Splitter);
|
||||
sb.Append(ChainProgress).Append(EventBingoModel.Splitter);
|
||||
sb.Append(ChainTaskTokenProgress).Append(EventBingoModel.Splitter);
|
||||
sb.Append(InflationRate);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static EventBingoPlayfabData Deserialize(string s)
|
||||
{
|
||||
if (s == null || s == "")
|
||||
return null;
|
||||
var tokens = s.Trim(EventBingoModel.Splitter).Split(EventBingoModel.Splitter);
|
||||
if (tokens.Length != TokenCount && tokens.Length != TokenCount - 1)
|
||||
{
|
||||
Debug.LogWarning($"[EventBingo]Wrong amount of parameters. Expect {TokenCount}, but got {tokens.Length} in \"{s}\".");
|
||||
return null;
|
||||
}
|
||||
var eventId = int.Parse(tokens[0]);
|
||||
var ticketCount = int.Parse(tokens[1]);
|
||||
var roundCount = int.Parse(tokens[2]);
|
||||
var stepInRound = int.Parse(tokens[3]);
|
||||
var seed = int.Parse(tokens[4]);
|
||||
var chainProgress = int.Parse(tokens[5]);
|
||||
var chainTaskTokenProgress = int.Parse(tokens[6]);
|
||||
var inflationRate = 0f;
|
||||
if (tokens.Length == TokenCount)
|
||||
{
|
||||
inflationRate = float.Parse(tokens[7]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Old data detected, missing inflation rate. Setting to 0.");
|
||||
}
|
||||
return new EventBingoPlayfabData(eventId, ticketCount, roundCount,
|
||||
stepInRound, seed, chainProgress, chainTaskTokenProgress, inflationRate);
|
||||
}
|
||||
|
||||
public EventBingoPlayfabData(int eventId, int ticketCount, int roundCount, int stepInRound,
|
||||
int seed, int chainProgress, int chainTaskTokenProgress, float inflationRate)
|
||||
{
|
||||
EventId = eventId;
|
||||
TicketCount = ticketCount;
|
||||
RoundCount = roundCount;
|
||||
StepInRound = stepInRound;
|
||||
Seed = seed;
|
||||
ChainProgress = chainProgress;
|
||||
ChainTaskTokenProgress = chainTaskTokenProgress;
|
||||
InflationRate = inflationRate;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
saveStore.Write(Key, Serialize()).RequireAccepted();
|
||||
}
|
||||
|
||||
public static EventBingoPlayfabData Load()
|
||||
{
|
||||
var s = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>().Read(Key).ValueOrThrow();
|
||||
return Deserialize(s);
|
||||
}
|
||||
}
|
||||
|
||||
public class EventBingoEntranceData
|
||||
{
|
||||
public bool NeedRedPoint;
|
||||
public int RedPointIndex;
|
||||
public DateTime ExpiryTime;
|
||||
public DateTime StartTime;
|
||||
public string Icon;
|
||||
}
|
||||
|
||||
public class EventBingoPlayerPreferenceData
|
||||
{
|
||||
public List<EventBingoNumberRollerItemData> RollerDataList;
|
||||
public bool IsFirstTime = true;
|
||||
private readonly static string Key = "EventBingo" + GContext.container.Resolve<IUserService>().UserId;
|
||||
|
||||
private string Serialize()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append(IsFirstTime ? "1" : "0").Append(EventBingoModel.Splitter);
|
||||
foreach (var data in RollerDataList)
|
||||
sb.Append(data.number).Append(EventBingoModel.Comma).Append(data.iconIdx).Append(EventBingoModel.Splitter);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static EventBingoPlayerPreferenceData Deserialize(string s)
|
||||
{
|
||||
var tokens = s.Trim(EventBingoModel.Splitter).Split(EventBingoModel.Splitter);
|
||||
var rollerData = new List<EventBingoNumberRollerItemData>();
|
||||
bool isFirstTime;
|
||||
if (tokens.Length < 1)
|
||||
{
|
||||
throw new Exception("[EventBingo] Invalid serialized data, length < 1");
|
||||
}
|
||||
isFirstTime = tokens[0] == "1";
|
||||
for (int i = 1; i < tokens.Length; i++)
|
||||
{
|
||||
var token = tokens[i];
|
||||
var tokenTokens = token.Split(EventBingoModel.Comma);
|
||||
if (tokenTokens.Length != 2)
|
||||
throw new Exception("[EventBingo] Wrong amount of parameters in " + token);
|
||||
rollerData.Add(new EventBingoNumberRollerItemData
|
||||
{
|
||||
number = int.Parse(tokenTokens[0]),
|
||||
iconIdx = int.Parse(tokenTokens[1])
|
||||
});
|
||||
}
|
||||
return new EventBingoPlayerPreferenceData { RollerDataList = rollerData, IsFirstTime = isFirstTime };
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
PlayerPrefs.SetString(Key, Serialize());
|
||||
}
|
||||
|
||||
public static EventBingoPlayerPreferenceData Load()
|
||||
{
|
||||
var s = PlayerPrefs.GetString(Key);
|
||||
if (s == null || s == "")
|
||||
return null;
|
||||
try
|
||||
{
|
||||
var res = Deserialize(s);
|
||||
return res;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class EventBingoTicketUpdateEvent { }
|
||||
|
||||
public class EventBingoUiData
|
||||
{
|
||||
public string InfoPanel { get; set; }
|
||||
public string NeoNormalPackPanel { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using SystemRandom = System.Random;
|
||||
using System.Text;
|
||||
using System;
|
||||
using UnityEngine.Assertions;
|
||||
using GameCore;
|
||||
using cfg;
|
||||
using System.Collections.Generic;
|
||||
using asap.core;
|
||||
|
||||
public class DrillBlockData
|
||||
{
|
||||
public ItemData Item { get; set; }
|
||||
public bool IsEmpty => Item == null || Item.id == -1 || (int)Item.count == 0;
|
||||
public static DrillBlockData CreateEmpty()
|
||||
{
|
||||
return new DrillBlockData();
|
||||
}
|
||||
}
|
||||
|
||||
public class DrillJobModel
|
||||
{
|
||||
public int Cost { get; set; }
|
||||
public DrillBlockData[] Blocks { get; set; }
|
||||
}
|
||||
|
||||
public class EventBreakTaskGenerationInfo
|
||||
{
|
||||
public int TableId { get; set; }
|
||||
public int CurrentGemProgress { get; set; }
|
||||
public int TargetItemId { get; set; }
|
||||
}
|
||||
|
||||
public class EventBreakTaskInfo
|
||||
{
|
||||
public int TaskId { get; set; }
|
||||
public int TargetItemId { get; set; }
|
||||
public ItemData Reward { get; set; }
|
||||
public int TotalGemRequired { get; set; }
|
||||
public int CurrentGemProgress { get; set; }
|
||||
}
|
||||
|
||||
public class DrillModel : IHasChainPack
|
||||
{
|
||||
public int EventId { get; set; }
|
||||
public int ChainPackID { get; set; }
|
||||
public float InflationRate { get; set; }
|
||||
|
||||
public int TicketCount { get; private set; }
|
||||
public DrillJobModel[] DrillJobList { get; set; }
|
||||
public EventBreakTaskInfo[] TaskInfoList { get; set; }
|
||||
public int[] DrillJobSeeds { get; set; }
|
||||
public int ChainPackProgress;
|
||||
public void Update(int eventId, EventBreakPfData pfData = null) // new event
|
||||
{
|
||||
if (pfData == null || pfData.EventId != eventId)
|
||||
{
|
||||
GenSeeds();
|
||||
InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
EventBreakAct.Ctx.GetFreshNewModelData(DrillJobSeeds, out var welcomeGift, out var drillJobs, out var taskInfoList);
|
||||
EventId = eventId;
|
||||
TicketCount = welcomeGift;
|
||||
TaskInfoList = taskInfoList;
|
||||
DrillJobList = drillJobs;
|
||||
ChainPackProgress = 0;
|
||||
ChainPackID = EventBreakAct.Ctx.GetChainPackID();
|
||||
ToPlayfabData().Save();
|
||||
return;
|
||||
}
|
||||
// var playerPreferenceData = EventBreakPlayerPreferenceData.Get(EventBreakAct.Ctx.TunnelCount);
|
||||
EventId = eventId;
|
||||
ChainPackID = pfData.ChainPackID;
|
||||
TicketCount = pfData.TicketCount;
|
||||
InflationRate = pfData.InflationRate;
|
||||
DrillJobSeeds = pfData.DrillJobSeeds;
|
||||
TaskInfoList = EventBreakAct.Ctx.GetTaskInfoArray(pfData.TaskData);
|
||||
DrillJobList = EventBreakAct.Ctx.GetDrillJobModels(pfData.DrillJobSeeds);
|
||||
ChainPackProgress = pfData.ChainPackProgress;
|
||||
return;
|
||||
}
|
||||
|
||||
private void GenSeeds()
|
||||
{
|
||||
DrillJobSeeds ??= new int[EventBreakAct.Ctx.TunnelCount];
|
||||
for (int i = 0; i < DrillJobSeeds.Length; i++)
|
||||
DrillJobSeeds[i] = 0;
|
||||
// ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public void UpdateSeed(int idx)
|
||||
{
|
||||
// DrillJobSeeds ??= new int[EventBreakAct.Ctx.TunnelCount];
|
||||
if (EventBreakAct.Ctx.DoesSeedNeedInc(idx, DrillJobSeeds[idx]))
|
||||
{
|
||||
DrillJobSeeds[idx]++;
|
||||
ToPlayfabData().Save();
|
||||
return;
|
||||
}
|
||||
Assert.IsTrue(idx >= 0 && idx < DrillJobSeeds.Length,
|
||||
$"[EventBreak] Cannot genenrate seed, idx {idx} is out of range [0, {DrillJobSeeds.Length}]");
|
||||
var seed = DateTime.Now.Millisecond;
|
||||
DrillJobSeeds[idx] = new SystemRandom(seed).Next();
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public bool AddTicket(int add)
|
||||
{
|
||||
if (TicketCount + add < 0)
|
||||
return false;
|
||||
TicketCount += add;
|
||||
ToPlayfabData().Save();
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(EventId, TicketCount);
|
||||
EventBreakAct.EventAggregator.Publish(new EventTicketUpdate());
|
||||
return true;
|
||||
}
|
||||
|
||||
public EventBreakPfData ToPlayfabData()
|
||||
{
|
||||
return new EventBreakPfData
|
||||
{
|
||||
EventId = EventId,
|
||||
ChainPackID = ChainPackID,
|
||||
InflationRate = InflationRate,
|
||||
TicketCount = TicketCount,
|
||||
TaskData = TaskInfoList.Select(i => (i.TaskId, i.CurrentGemProgress)).ToArray(),
|
||||
ChainPackProgress = ChainPackProgress,
|
||||
DrillJobSeeds = DrillJobSeeds
|
||||
};
|
||||
}
|
||||
|
||||
public void SetChainProgress(int p)
|
||||
{
|
||||
ChainPackProgress = p;
|
||||
}
|
||||
|
||||
public int GetChainProgress()
|
||||
{
|
||||
return ChainPackProgress;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public GenericChainPackData<DrillModel> ToChainPackData()
|
||||
{
|
||||
return GenericChainPackData<DrillModel>.Create(this, EventBreakAct.Ctx.GetPackData(ChainPackID));
|
||||
}
|
||||
}
|
||||
|
||||
public class EventBreakPfData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int EventId { get; set; }
|
||||
public int TicketCount { get; set; }
|
||||
public int ChainPackProgress { get; set; }
|
||||
public float InflationRate { get; set; }
|
||||
public int ChainPackID { get; set; }
|
||||
public (int id, int progress)[] TaskData { get; set; }
|
||||
public int[] DrillJobSeeds { get; set; }
|
||||
private const string Splitter = ",", Comma = "|";
|
||||
public const string PfKey = "EventBreak";
|
||||
/// <summary>
|
||||
/// Temporary stash.
|
||||
/// </summary>
|
||||
// public static EventBreakPfData Stash = null;
|
||||
//private const int TokenCount = 13;
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var s = new StringBuilder();
|
||||
s.Append(EventId + Splitter);
|
||||
s.Append(TicketCount + Splitter);
|
||||
s.Append(ChainPackProgress + Splitter);
|
||||
foreach (var (id, progress) in TaskData)
|
||||
s.Append(id + Splitter + progress + Splitter);
|
||||
foreach (var seed in DrillJobSeeds)
|
||||
s.Append(seed + Splitter);
|
||||
s.Append(InflationRate + Splitter);
|
||||
s.Append(ChainPackID + Splitter);
|
||||
return s.ToString();
|
||||
}
|
||||
|
||||
public static EventBreakPfData Deserialize(string s)
|
||||
{
|
||||
if (s == null)
|
||||
{
|
||||
Debug.Log($"[EventBreak]Null string.");
|
||||
return null;
|
||||
}
|
||||
var res = new EventBreakPfData();
|
||||
var tokens = s.Trim(Splitter[0]).Split(Splitter);
|
||||
if (tokens.Length < 12)
|
||||
{
|
||||
Debug.Log($"[EventBreak]Wrong string format: string \"{s}\" has {tokens.Length} tokens, and the expected number is 14 or 13 or 12");
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
res.EventId = int.Parse(tokens[0]);
|
||||
res.TicketCount = int.Parse(tokens[1]);
|
||||
res.ChainPackProgress = int.Parse(tokens[2]);
|
||||
res.TaskData = new (int id, int progress)[3];
|
||||
int i = 3;
|
||||
while (i < 9)
|
||||
{
|
||||
res.TaskData[i / 2 - 1] = (int.Parse(tokens[i]), int.Parse(tokens[i + 1]));
|
||||
i += 2;
|
||||
}
|
||||
res.DrillJobSeeds = new int[3];
|
||||
while (i < 12)
|
||||
{
|
||||
res.DrillJobSeeds[i - 9] = int.Parse(tokens[i]);
|
||||
i++;
|
||||
}
|
||||
if (tokens.Length >= 13)
|
||||
{
|
||||
res.InflationRate = float.Parse(tokens[12]);
|
||||
if (tokens.Length >= 14)
|
||||
{
|
||||
res.ChainPackID = int.Parse(tokens[13]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Old data detected, missing inflation rate. Setting to 0.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Log($"[EventBreak]Possible parse error: {e.Message}\n{e.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
saveStore.Write(PfKey, s).RequireAccepted();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public class EventBreakPlayerPreferenceDataLegacy
|
||||
{
|
||||
public int[] Seeds { get; set; }
|
||||
private const string Splitter = ",", PpKey = "EventBreakCache";
|
||||
|
||||
public static EventBreakPlayerPreferenceDataLegacy Get(int seedCount = 3)
|
||||
{
|
||||
var s = PlayerPrefs.GetString(PpKey);
|
||||
if (s == "")
|
||||
return GenSeeds(seedCount);
|
||||
var res = Deserialize(s);
|
||||
if (res == null)
|
||||
return GenSeeds(seedCount);
|
||||
return res;
|
||||
}
|
||||
|
||||
private static EventBreakPlayerPreferenceDataLegacy GenSeeds(int count)
|
||||
{
|
||||
var seeds = new int[count];
|
||||
var rng = new SystemRandom(DateTime.Now.Millisecond);
|
||||
for (int i = 0; i < count; i++)
|
||||
seeds[i] = rng.Next();
|
||||
return new EventBreakPlayerPreferenceDataLegacy
|
||||
{
|
||||
Seeds = seeds
|
||||
};
|
||||
}
|
||||
|
||||
private static EventBreakPlayerPreferenceDataLegacy Deserialize(string s)
|
||||
{
|
||||
var tokens = s.Trim(Splitter[0]).Split(Splitter);
|
||||
var seeds = new int[tokens.Length];
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < tokens.Length; i++)
|
||||
seeds[i] = int.Parse(tokens[i]);
|
||||
return new EventBreakPlayerPreferenceDataLegacy
|
||||
{
|
||||
Seeds = seeds
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Log($"[EventBreak]Possible parse error: {e.Message}\n{e.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var seed in Seeds)
|
||||
{
|
||||
sb.Append(seed + Splitter);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
PlayerPrefs.SetString(PpKey, s);
|
||||
}
|
||||
}
|
||||
|
||||
public class EventBreakUIData
|
||||
{
|
||||
public string MainPanelUrl { get; set; }
|
||||
public string InfoPanelUrl { get; set; }
|
||||
public string ChainPackPanelUrl { get; set; }
|
||||
public string NormalPackPanelUrl { get; set; }
|
||||
public string RewardPanelUrl { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class EventBreakChainPackGenerationInfo
|
||||
{
|
||||
public List<int> ChainList { get; set; }
|
||||
public DateTime ExpireTime { get; set; }
|
||||
public Pack[] Packs { get; set; }
|
||||
}
|
||||
|
||||
// public class EventBreakChainPackData : IChainPackData
|
||||
// {
|
||||
// private const int SlotCount = 6;
|
||||
// private List<int> ChainList { get; set; }
|
||||
// private Pack[] Packs { get; set; }
|
||||
// public bool IsEndGame => ChainProgress > ChainList.Count - SlotCount;
|
||||
// public int ChainListCount => ChainList.Count;
|
||||
// public int ChainProgress { get; set; }
|
||||
// public int EventId { get; set; }
|
||||
// private DateTime ExpireTime { get; set; }
|
||||
// public TimeSpan RemainingTime => ExpireTime - ZZTimeHelper.UtcNow();
|
||||
|
||||
// public bool IsChainPackDepleted => ChainProgress >= ChainListCount;
|
||||
|
||||
// public bool DoNeedPackRedPoint
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// if (!IsChainPackDepleted && ChainProgress < ChainListCount)
|
||||
// return Packs[ChainProgress].IAPID == 0;
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// public string RedPointKey => "eventbreak.pack";
|
||||
|
||||
// public EventBreakChainPackData(EventBreakChainPackGenerationInfo info, int eventId, ref int chainProgress)
|
||||
// {
|
||||
// ChainList = info.ChainList;
|
||||
// ChainProgress = chainProgress;
|
||||
// EventId = eventId;
|
||||
// ExpireTime = info.ExpireTime;
|
||||
// Packs = info.Packs;
|
||||
// }
|
||||
|
||||
// public Pack GetChainPackByChainProgress(int chainProgress)
|
||||
// {
|
||||
// // Debug.Log($"[EventBreak] chainProgress: {chainProgress}");
|
||||
// return Packs[chainProgress];
|
||||
// }
|
||||
|
||||
// public int GetChainProgressBySlotIdx(int slotIdx)
|
||||
// {
|
||||
// int res;
|
||||
// if (ChainProgress > ChainList.Count - SlotCount)
|
||||
// res = ChainList.Count - SlotCount + slotIdx;
|
||||
// else
|
||||
// res = ChainProgress + slotIdx;
|
||||
// Assert.IsTrue(res < ChainList.Count, $"Progress {res} out of range: {ChainListCount}");
|
||||
// // Debug.Log($"[EventBreak] slotIdx: {slotIdx}, chainProgress: {res}");
|
||||
// return res;
|
||||
// }
|
||||
|
||||
// // Is this needed or what?
|
||||
// public List<int> GetTokenProgressRewardAfterAddingToken(int tokenAdded)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
|
||||
// public void UploadData()
|
||||
// {
|
||||
// EventBreakAct.DrillModel.ChainPackProgress = ChainProgress;
|
||||
// EventBreakAct.DrillModel.ToPlayfabData().Save();
|
||||
// }
|
||||
|
||||
// public void OnBuySuccess()
|
||||
// {
|
||||
// ChainProgress++;
|
||||
// UploadData();
|
||||
// }
|
||||
|
||||
// public List<ItemData> GetItemsByPackDropId(int dropId)
|
||||
// {
|
||||
// throw new NotImplementedException();
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,564 @@
|
||||
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, ChainPackID;
|
||||
public float InflationRate;
|
||||
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;
|
||||
InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
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;
|
||||
ChainPackID = GContext.container.Resolve<EventCanTableContext>().GetChainPackID();
|
||||
ToPlayerPreferenceData().Save();
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
else //read from playfab and playerprefs
|
||||
{
|
||||
EventId = eventId;
|
||||
ChainPackID = pfData.ChainPackID;
|
||||
TicketCount = pfData.TicketCount;
|
||||
InflationRate = pfData.InflationRate;
|
||||
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,
|
||||
ChainPackID = ChainPackID,
|
||||
InflationRate = InflationRate,
|
||||
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);
|
||||
PlayerItemData playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
playerItemData.ItemTransition(res);
|
||||
if (ItemId == 1001)
|
||||
{
|
||||
playerItemData.ItemInflation(res, GContext.container.Resolve<EventCanModel>().InflationRate);
|
||||
}
|
||||
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);
|
||||
info.Reward.origin = Reward.origin;
|
||||
info.Reward.inflate = Reward.inflate;
|
||||
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
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
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, ChainPackID;
|
||||
/// <summary>
|
||||
/// GemTaskId, CurrentGemProgress
|
||||
/// </summary>
|
||||
public Dictionary<int, int> GemTaskInfo;
|
||||
/// <summary>
|
||||
/// RewardId, DrawCount
|
||||
/// </summary>
|
||||
public Dictionary<int, int> DrawsSinceLastRewards;
|
||||
public float InflationRate;
|
||||
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(Comma)
|
||||
.Append(InflationRate).Append(Comma)
|
||||
.Append(ChainPackID).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]);
|
||||
var token = tokens[6].Split(Comma);
|
||||
res.ProgressTaskIdx = int.Parse(token[0]);
|
||||
res.ProgressTaskProgress = int.Parse(token[1]);
|
||||
if (token.Length >= 3)
|
||||
{
|
||||
res.InflationRate = float.Parse(token[2]);
|
||||
if (token.Length >= 4)
|
||||
res.ChainPackID = int.Parse(token[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Old data detected, missing inflation rate. Setting to 0.");
|
||||
}
|
||||
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();
|
||||
saveStore.Write(PlayFabKey, s).RequireAccepted();
|
||||
}
|
||||
|
||||
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 int RedPointIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
int res = 0;
|
||||
for (int i = ChainProgress; i < ChainListCount; i++)
|
||||
{
|
||||
if (Packs[i].IAPID == 0)
|
||||
res++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,350 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using GameCore;
|
||||
using asap.core;
|
||||
using System.Linq;
|
||||
|
||||
public class EventGatherCoreData : IHasChainPack
|
||||
{
|
||||
/// <summary>
|
||||
/// How many stages has been played.
|
||||
/// </summary>
|
||||
private int _ticketCount, _stageCount, _eventId, _chainProgress;
|
||||
float _inflationRate;
|
||||
public float InflationRate => _inflationRate;
|
||||
public int StageCount => _stageCount;
|
||||
public int TicketCount => _ticketCount;
|
||||
public int EventId => _eventId;
|
||||
public int ChainPackID { get; set; }
|
||||
public EventGatherCoreStageData StageData;
|
||||
public EventGatherCoreData() { }
|
||||
|
||||
public static EventGatherCoreData CreateNew(cfg.FishingEvent e)
|
||||
{
|
||||
var res = new EventGatherCoreData();
|
||||
res._stageCount = 0;
|
||||
res.StageData = EventGatherCoreStageData.CreateNew();
|
||||
res._eventId = e.ID;
|
||||
return res;
|
||||
}
|
||||
|
||||
public void InitNew(int eventId)
|
||||
{
|
||||
_stageCount = 0;
|
||||
_ticketCount = EventGatherCoreAct.Ctx.GetWelcomeGift();
|
||||
ChainPackID = EventGatherCoreAct.Ctx.GetChainPackID();
|
||||
_eventId = eventId;
|
||||
StageData = EventGatherCoreStageData.CreateNew();
|
||||
StageData.MoveToStage(0);
|
||||
_chainProgress = 0;
|
||||
_inflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
}
|
||||
|
||||
public void LoadData(EventGatherCorePlayfabData data)
|
||||
{
|
||||
_eventId = data.EventId;
|
||||
_ticketCount = data.TicketCount;
|
||||
ChainPackID = data.ChainPackID;
|
||||
_stageCount = data.StageCount;
|
||||
StageData = EventGatherCoreStageData.CreateNew();
|
||||
StageData.Init(data.StageCount, data.RewardClaimStateFlag, data.TriggerItemStateFlag);
|
||||
_chainProgress = data.ChainProgress;
|
||||
_inflationRate = data.InflationRate;
|
||||
}
|
||||
|
||||
public void MoveToNextStage()
|
||||
{
|
||||
_stageCount++;
|
||||
StageData.MoveToStage(StageCount);
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public void AddTicket(int n)
|
||||
{
|
||||
_ticketCount += n;
|
||||
if (_ticketCount < 0)
|
||||
{
|
||||
_ticketCount -= n;
|
||||
}
|
||||
ToPlayfabData().Save();
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(_eventId, _ticketCount);
|
||||
GContext.Publish(new EventTicketUpdate());
|
||||
}
|
||||
|
||||
public EventGatherCorePlayfabData ToPlayfabData()
|
||||
{
|
||||
var res = new EventGatherCorePlayfabData
|
||||
{
|
||||
EventId = _eventId,
|
||||
TicketCount = _ticketCount,
|
||||
StageCount = _stageCount,
|
||||
ChainPackID = ChainPackID,
|
||||
RewardClaimStateFlag = StageData.RewardClaimStateFlag,
|
||||
TriggerItemStateFlag = StageData.TriggerItemStateFlag,
|
||||
ChainProgress = _chainProgress,
|
||||
InflationRate = _inflationRate
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
public EventBingoEntranceData ToEntranceData()
|
||||
{
|
||||
var res = new EventBingoEntranceData();
|
||||
var ctx = EventGatherCoreAct.Ctx;
|
||||
var t = ctx.GetEventStartTimeAndEndTime();
|
||||
var chainPackData = GenericChainPackData<EventGatherCoreData>.Create(this, ctx.GetChainPackInfo(ChainPackID));
|
||||
res.StartTime = t.Item1;
|
||||
res.ExpiryTime = t.Item2;
|
||||
res.Icon = ctx.GetEntranceIcon();
|
||||
res.NeedRedPoint = GContext.container.Resolve<EventGatherCoreData>().TicketCount >= ctx.GetRedPointThreshold();
|
||||
res.RedPointIndex = _ticketCount + chainPackData.RedPointIndex;
|
||||
return res;
|
||||
}
|
||||
|
||||
#region ChainPack
|
||||
public void SetChainProgress(int p)
|
||||
{
|
||||
_chainProgress = p;
|
||||
}
|
||||
|
||||
public int GetChainProgress()
|
||||
{
|
||||
return _chainProgress;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
public GenericChainPackData<EventGatherCoreData> ToChainPackData()
|
||||
{
|
||||
return GenericChainPackData<EventGatherCoreData>.Create(this, EventGatherCoreAct.Ctx.GetChainPackInfo(ChainPackID));
|
||||
}
|
||||
#endregion
|
||||
|
||||
public TimerContext ToTimerContext(System.Action onExpire)
|
||||
{
|
||||
var ctx = EventGatherCoreAct.Ctx;
|
||||
var t = ctx.GetEventStartTimeAndEndTime();
|
||||
var res = new TimerContext
|
||||
{
|
||||
ExpiryTime = t.Item2,
|
||||
StartTime = t.Item1,
|
||||
OnExpire = onExpire
|
||||
};
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventGatherCoreStageData
|
||||
{
|
||||
public List<int> RewardIdList = new List<int>();
|
||||
public long RewardClaimStateFlag, TriggerItemStateFlag;
|
||||
public int StageId;
|
||||
// public int CoreItemReceivedCount => RewardIdList.Count((id, idx) => EventGatherCoreTableContext.CoreItemIndicator == id && IsRewardClaimed(idx));
|
||||
public int CoreItemReceivedCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = 0;
|
||||
if (RewardIdList == null)
|
||||
return result;
|
||||
result = RewardIdList.Where((id, idx) => EventGatherCoreAct.Ctx.IsCoreItem(id) && IsRewardClaimed(idx)).Count();
|
||||
// Debug.Log($"[EventGatherCore] CoreItemReceivedCount: {result}");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
private EventGatherCoreStageData() { }
|
||||
|
||||
public static EventGatherCoreStageData CreateNew()
|
||||
{
|
||||
var res = new EventGatherCoreStageData();
|
||||
res.MoveToStage(0);
|
||||
return res;
|
||||
}
|
||||
|
||||
public int PlayCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int res = 0;
|
||||
long flag = RewardClaimStateFlag;
|
||||
while (flag > 0)
|
||||
{
|
||||
if ((flag & 1) != 0)
|
||||
{
|
||||
res++;
|
||||
}
|
||||
flag >>= 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public void Init(int roundCount, long rewardClaimStateFlag = 0L, long triggerItemStateFlag = 0L)
|
||||
{
|
||||
// Debug.Log($"[EventGatherCore]Move to stage {roundCount}");
|
||||
RewardClaimStateFlag = rewardClaimStateFlag;
|
||||
TriggerItemStateFlag = triggerItemStateFlag;
|
||||
StageId = EventGatherCoreAct.Ctx.GetStageId(roundCount);
|
||||
RewardIdList = EventGatherCoreAct.Ctx.GetRewardIdList(StageId);
|
||||
}
|
||||
|
||||
public void MoveToStage(int roundCount)
|
||||
{
|
||||
// Debug.Log($"[EventGatherCore]Move to stage {roundCount}");
|
||||
var ctx = EventGatherCoreAct.Ctx;
|
||||
StageId = ctx.GetStageId(roundCount);
|
||||
RewardIdList = ctx.GetStageInfo(StageId);
|
||||
RewardClaimStateFlag = 0L;
|
||||
TriggerItemStateFlag = 0L;
|
||||
}
|
||||
|
||||
public bool IsRewardClaimed(int rewardIdx)
|
||||
{
|
||||
return (RewardClaimStateFlag & (1L << rewardIdx)) != 0;
|
||||
}
|
||||
|
||||
public bool IsTriggerUsed(int triggerIdx)
|
||||
{
|
||||
return (TriggerItemStateFlag & (1L << triggerIdx)) != 0;
|
||||
}
|
||||
|
||||
public bool IsEarlyTry(EventGatherCoreTableContext ctx)
|
||||
{
|
||||
return PlayCount < ctx.GetStageMinAttemptCount(StageId) - 1;
|
||||
}
|
||||
|
||||
public bool IsCloseToFinish(EventGatherCoreTableContext ctx)
|
||||
{
|
||||
return CoreItemReceivedCount == ctx.GetCoreItemTargetCount(StageId) - 1;
|
||||
}
|
||||
|
||||
public void SetRewardClaimed(int rewardIdx)
|
||||
{
|
||||
RewardClaimStateFlag |= 1L << rewardIdx;
|
||||
}
|
||||
|
||||
public void SetTriggerUsed(int triggerIdx)
|
||||
{
|
||||
TriggerItemStateFlag |= 1L << triggerIdx;
|
||||
}
|
||||
// public void AddCoreItem(int num = 1)
|
||||
// {
|
||||
// CoreItemReceivedCount += num;
|
||||
// }
|
||||
|
||||
public bool IsStageCleared(EventGatherCoreTableContext ctx)
|
||||
{
|
||||
long fullFlag = (1L << ctx.GetStageInfo(StageId).Count) - 1;
|
||||
return CoreItemReceivedCount >= ctx.GetCoreItemTargetCount(StageId) || (RewardClaimStateFlag & fullFlag) == fullFlag;
|
||||
}
|
||||
|
||||
public List<int> GetRemainingRewardIds()
|
||||
{
|
||||
return RewardIdList.Where((id, idx) => ((1 << idx) & RewardClaimStateFlag) == 0).ToList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class EventGatherCoreTriggerTaken
|
||||
{
|
||||
public bool Succeeded;
|
||||
public int RewardIdx;
|
||||
public int RewardId;
|
||||
public Vector2 Position;
|
||||
}
|
||||
|
||||
public class EventGatherCoreRewardInfo
|
||||
{
|
||||
public int Idx;
|
||||
public ItemData ItemData;
|
||||
public bool IsSpecial = false;
|
||||
public bool IsClaimed;
|
||||
}
|
||||
|
||||
public class EventGatherCorePlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int EventId { get; set; }
|
||||
public int TicketCount { get; set; }
|
||||
public int ChainPackID { get; set; }
|
||||
public int StageCount { get; set; }
|
||||
public long RewardClaimStateFlag { get; set; }
|
||||
public long TriggerItemStateFlag { get; set; }
|
||||
public int ChainProgress { get; set; }
|
||||
public float InflationRate { get; set; }
|
||||
public static readonly string Key = "EventGatherCoreData";
|
||||
private readonly char Splitter = '|';
|
||||
private readonly int TokenCount = 7;
|
||||
|
||||
private string Serialize()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append(EventId).Append(Splitter);
|
||||
sb.Append(TicketCount).Append(Splitter);
|
||||
sb.Append(StageCount).Append(Splitter);
|
||||
sb.Append(RewardClaimStateFlag).Append(Splitter);
|
||||
sb.Append(TriggerItemStateFlag).Append(Splitter);
|
||||
sb.Append(ChainProgress).Append(Splitter);
|
||||
sb.Append(InflationRate).Append(Splitter);
|
||||
sb.Append(ChainPackID);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private void Deserialize(string data)
|
||||
{
|
||||
var tokens = data.Trim(Splitter).Split(Splitter);
|
||||
if (tokens.Length < 7)
|
||||
throw new System.Exception($"[EventGatherCore]Wrong amount of parameters. Expect {TokenCount}, but got {tokens.Length} in \"{data}\".");
|
||||
EventId = int.Parse(tokens[0]);
|
||||
TicketCount = int.Parse(tokens[1]);
|
||||
StageCount = int.Parse(tokens[2]);
|
||||
RewardClaimStateFlag = long.Parse(tokens[3]);
|
||||
TriggerItemStateFlag = long.Parse(tokens[4]);
|
||||
ChainProgress = int.Parse(tokens[5]);
|
||||
|
||||
InflationRate = float.Parse(tokens[6]);
|
||||
if (tokens.Length > 7)
|
||||
{
|
||||
ChainPackID = int.Parse(tokens[7]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
saveStore.Write(Key, Serialize()).RequireAccepted();
|
||||
}
|
||||
|
||||
public static EventGatherCorePlayfabData Load()
|
||||
{
|
||||
var s = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>().Read(Key).ValueOrThrow();
|
||||
var res = new EventGatherCorePlayfabData();
|
||||
try
|
||||
{
|
||||
res.Deserialize(s);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogWarning($"[EventGatherCore]Error when deserializing EventGatherCoreData: {e}");
|
||||
return null;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class EventGatherCoreUiData
|
||||
{
|
||||
public string MainPanelUrl { get; set; }
|
||||
public string InfoPanelUrl { get; set; }
|
||||
public string ChainPackPanelUrl { get; set; }
|
||||
public string NormalPackPanelUrl { get; set; }
|
||||
public string RewardPopupPanelUrl { get; set; }
|
||||
}
|
||||
|
||||
public class EventGatherCoreRewardPopupPanelClose
|
||||
{
|
||||
public ItemData Reward { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
/// <summary>
|
||||
/// 同一系列的活动使用同一个Model
|
||||
/// 礼包、代币、进度奖励等数据结构都相同
|
||||
/// 后续活动只用关系自己的玩法数据即可
|
||||
/// </summary>
|
||||
public class EventLuckyGameModel : ILuckyTaskGetData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public static EventLuckyGameModel Get()
|
||||
{
|
||||
var model = GContext.container.Resolve<EventLuckyGameModel>();
|
||||
if (model == null)
|
||||
{
|
||||
GContext.container.Register<EventLuckyGameModel>().AsSingleton();
|
||||
model = GContext.container.Resolve<EventLuckyGameModel>();
|
||||
}
|
||||
return model;
|
||||
}
|
||||
public const string Key = "EventLuckGameRecord";
|
||||
public const string RedPointKey = "luckgame";
|
||||
EventLuckGameRecord eventLuckGameRecord = new EventLuckGameRecord();
|
||||
FishingEventData fishingEventData;
|
||||
Tables tables;
|
||||
public FishingEvent fishingEvent { get; private set; }
|
||||
public EventLuckGameInfo eventInfo { get; private set; } = new EventLuckGameInfo();
|
||||
public FishingEventCycleItem cycle { get; private set; }
|
||||
public int EventId => eventLuckGameRecord.EventId;
|
||||
public int Progress => eventLuckGameRecord.Progress;
|
||||
public string EventGameData => eventLuckGameRecord.EventGameData;
|
||||
public TimeSpan RemainingTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if (fishingEvent == null || fishingEvent.TimeDefinition is not LimitedTime limitedTime)
|
||||
{
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
return System.DateTime.Parse(limitedTime.EndTime) - ZZTimeHelper.UtcNow();
|
||||
}
|
||||
}
|
||||
|
||||
public EventLuckyGameModel(FishingEventData fishingEventData, Tables tables)
|
||||
{
|
||||
this.fishingEventData = fishingEventData ?? GContext.container.Resolve<FishingEventData>();
|
||||
this.tables = tables ?? GContext.container.Resolve<Tables>();
|
||||
luckyTaskData = new LuckyTaskData(this);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
SetRedPointState();
|
||||
|
||||
string s = Newtonsoft.Json.JsonConvert.SerializeObject(eventLuckGameRecord);
|
||||
//Debug.Log($"[EventLuckGameModel] Save EventLuckGameRecord: {s}");
|
||||
saveStore.Write(Key, s).RequireAccepted();
|
||||
}
|
||||
|
||||
//=========================以下是活动数据=========================
|
||||
public int TicketCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsActive)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
//_ticketCount = fishingEventData.GetTransitionDataCount(EventId);
|
||||
return eventLuckGameRecord.TicketCount;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (!IsActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
eventLuckGameRecord.TicketCount = value;
|
||||
if (eventLuckGameRecord.TicketCount < 0)
|
||||
{
|
||||
eventLuckGameRecord.TicketCount = 0;
|
||||
}
|
||||
fishingEventData.SaveTransitionData(EventId, eventLuckGameRecord.TicketCount);
|
||||
Save();
|
||||
}
|
||||
}
|
||||
void SetRedPointState()
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
if (luckyTaskData == null || eventLuckGameRecord == null)
|
||||
return;
|
||||
int count = 1;
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
var luckyCardsData = Newtonsoft.Json.JsonConvert.DeserializeObject<LuckyCardsData>(EventGameData);
|
||||
|
||||
if (luckyCardsData != null && luckyCardsData.pos.Count > 0)
|
||||
{
|
||||
var config = tables.TbEventLuckyCardsConfig.GetOrDefault(luckyCardsData.pos.Count + 1);
|
||||
if (config == null)
|
||||
{
|
||||
config = tables.TbEventLuckyCardsConfig.GetOrDefault(1); // 默认配置
|
||||
}
|
||||
count = config.ItemCost;
|
||||
}
|
||||
}
|
||||
RedPointManager.Instance.SetRedPointState(RedPointKey, eventLuckGameRecord.TicketCount >= count, eventLuckGameRecord.TicketCount + luckyTaskData.RedCount);
|
||||
}
|
||||
public bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fishingEvent.TimeDefinition is LimitedTime limitedTime)
|
||||
{
|
||||
return ZZTimeHelper.UtcNow() < System.DateTime.Parse(limitedTime.EndTime) && ZZTimeHelper.UtcNow() > System.DateTime.Parse(limitedTime.StartTime);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void AddTicket(int count)
|
||||
{
|
||||
if (fishingEvent == null || cycle == null || !IsActive)
|
||||
{
|
||||
Debug.LogError("[EventLuckGameModel] _tableEvent is null, cannot add ticket.");
|
||||
return;
|
||||
}
|
||||
TicketCount += count;
|
||||
}
|
||||
|
||||
#region 进度奖励
|
||||
public void AddProgress(int progress)
|
||||
{
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
Debug.LogError("[EventLuckGameModel] _tableEvent is null, cannot add progress.");
|
||||
return;
|
||||
}
|
||||
eventLuckGameRecord.Progress += progress;
|
||||
if (eventLuckGameRecord.Progress < 0)
|
||||
{
|
||||
eventLuckGameRecord.Progress = 0;
|
||||
}
|
||||
Save();
|
||||
}
|
||||
|
||||
#endregion 进度奖励
|
||||
|
||||
public void OnEvent(FishingEvent t)
|
||||
{
|
||||
fishingEvent = t;
|
||||
if (t == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
string s = saveStore.Read(Key).ValueOrThrow();
|
||||
if (s == null)
|
||||
{
|
||||
eventLuckGameRecord = new EventLuckGameRecord();
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
eventLuckGameRecord = Newtonsoft.Json.JsonConvert.DeserializeObject<EventLuckGameRecord>(s) ?? throw new Newtonsoft.Json.JsonSerializationException("Null luck-game save.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventLuckGameModel] Deserialize Error: {e.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
DicTasks = eventLuckGameRecord.DicTasks;
|
||||
cycle = tables.TbFishingEventCycleItem.GetOrDefault(t.RedirectID);
|
||||
if (cycle == null)
|
||||
{
|
||||
Debug.LogError($"[EventLuckGameModel] CycleItem3 not found for RedirectID: {t.RedirectID}");
|
||||
return;
|
||||
}
|
||||
if (eventLuckGameRecord.EventId != t.ID)
|
||||
{
|
||||
eventLuckGameRecord.EventId = t.ID;
|
||||
eventLuckGameRecord.ChainProgress = 0;
|
||||
eventLuckGameRecord.Progress = 0;
|
||||
eventLuckGameRecord.EventGameData = string.Empty;
|
||||
TicketCount = cycle.WelcomeGift;
|
||||
DicTasks.Clear();
|
||||
}
|
||||
SetEventInfo();
|
||||
}
|
||||
|
||||
//=========================以上是活动数据=========================
|
||||
//=========================以下是玩法数据=========================
|
||||
public void SetEventGameData(string data)
|
||||
{
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
Debug.LogError("[EventLuckyGameModel] fishingEvent is null, cannot set event game data.");
|
||||
return;
|
||||
}
|
||||
eventLuckGameRecord.EventGameData = data;
|
||||
Save();
|
||||
}
|
||||
void SetEventInfo()
|
||||
{
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
Debug.LogError("[EventLuckyGameModel] fishingEvent is null, cannot set event info.");
|
||||
return;
|
||||
}
|
||||
eventInfo = GetEventLuckGameInfo();
|
||||
|
||||
if (DicTasks.Count == 0)
|
||||
{
|
||||
luckyTaskData.InitTasks();
|
||||
}
|
||||
else
|
||||
{
|
||||
luckyTaskData.SetRedPoint();
|
||||
}
|
||||
SetRedPointState();
|
||||
}
|
||||
|
||||
EventLuckGameInfo GetEventLuckGameInfo()
|
||||
{
|
||||
var eventInfo = new EventLuckGameInfo();
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
EventLuckyCardsMain _tableMain = tables.TbEventLuckyCardsMain.GetOrDefault(cycle.RedirectID);
|
||||
if (_tableMain == null)
|
||||
{
|
||||
Debug.LogError($"[EventLuckyCardsMain] EventLuckyCardsMain is Null RedirectID {cycle.RedirectID}");
|
||||
fishingEvent = null;
|
||||
return null;
|
||||
}
|
||||
eventInfo.IconName = _tableMain.Icon;
|
||||
eventInfo.MainPrefab = _tableMain.Prefab;
|
||||
eventInfo.TaskPanelName = _tableMain.TaskPrefab;
|
||||
eventInfo.ItemID = _tableMain.Item;
|
||||
eventInfo.PackId = _tableMain.PackId;
|
||||
}
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
#region 任务相关数据获取接口
|
||||
//这是一个工具类,只有一个监听任务触发的状态,处理任务数据
|
||||
private LuckyTaskData luckyTaskData;
|
||||
/// 获取任务面板名称,配表
|
||||
public string TaskPanelName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DicTasks.GetValueOrDefault(-4, 0) == 1)
|
||||
{
|
||||
return "EventLuckyCardsADTaskPopupPanel";
|
||||
}
|
||||
return eventInfo.TaskPanelName;
|
||||
}
|
||||
}
|
||||
public string LuckyTaskRedPointKey => "luckgame.luckytask";
|
||||
//任务数据存储
|
||||
public Dictionary<int, int> DicTasks { get; set; } = new Dictionary<int, int>();
|
||||
|
||||
public void InitDicTasks()
|
||||
{
|
||||
luckyTaskData.RefreshTasks();
|
||||
}
|
||||
|
||||
public void SaveTaskData()
|
||||
{
|
||||
eventLuckGameRecord.DicTasks = DicTasks;
|
||||
Save();
|
||||
}
|
||||
public bool OnGetTaskReward(int id)
|
||||
{
|
||||
return luckyTaskData.OnGetTaskReward(id);
|
||||
}
|
||||
|
||||
public int GetLuckyPackID()
|
||||
{
|
||||
if (fishingEvent == null || cycle == null || eventInfo == null)
|
||||
{
|
||||
Debug.LogError("[EventLuckyGameModel] fishingEvent or cycle is null, cannot get lucky gift info.");
|
||||
return 0;
|
||||
}
|
||||
return eventInfo.PackId;
|
||||
}
|
||||
|
||||
#endregion 任务相关数据获取接口
|
||||
//=========================以上是玩法数据=========================
|
||||
public void Dispose()
|
||||
{
|
||||
luckyTaskData.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public class EventLuckGameRecord
|
||||
{
|
||||
public int EventId { get; set; }
|
||||
public int ChainProgress { get; set; }
|
||||
public int Progress { get; set; }
|
||||
public int TicketCount { get; set; } = 0;
|
||||
public string EventGameData { get; set; } = "";
|
||||
public Dictionary<int, int> DicTasks { get; set; } = new Dictionary<int, int>();
|
||||
|
||||
}
|
||||
|
||||
public class EventLuckGameInfo
|
||||
{
|
||||
public string ActName = "EventLuckyCardsAct";
|
||||
public string IconName;
|
||||
public string MainPrefab;
|
||||
public string TaskPanelName;
|
||||
public int ItemID;
|
||||
public int PackId;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,354 @@
|
||||
using System.Text;
|
||||
using asap.core;
|
||||
using GameCore;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class EventOfferJourneyData : AEventOfferJourneyData
|
||||
{
|
||||
private int _progress, _eventId, _ticketCount, _vipLvlOnTrigger, _roundCount;
|
||||
private float _inflationRate;
|
||||
private EventOfferJourneyTableContext _ctx;
|
||||
public override int Progress => _progress;
|
||||
public override int TicketCount => _ticketCount;
|
||||
public override int EventId => _eventId;
|
||||
public override float InflationRate => _inflationRate;
|
||||
public override ShopBuyType BuyType => ShopBuyType.EventPack;
|
||||
public int VipLvlOnTrigger => _vipLvlOnTrigger;
|
||||
|
||||
public void Init(int eventId, int vipLvl)
|
||||
{
|
||||
_progress = 0;
|
||||
_eventId = eventId;
|
||||
_ticketCount = 0;
|
||||
_vipLvlOnTrigger = vipLvl;
|
||||
_roundCount = 0;
|
||||
_ctx = EventOfferJourneyTableContext.CreateNew(eventId);
|
||||
_inflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
}
|
||||
|
||||
public void InitTest()
|
||||
{
|
||||
_progress = 0;
|
||||
_eventId = 90500001;
|
||||
_ticketCount = 114514;
|
||||
_vipLvlOnTrigger = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
_roundCount = 0;
|
||||
_ctx = EventOfferJourneyTableContext.GetTestSample();
|
||||
}
|
||||
|
||||
private EventOfferJourneyPlayfabData ToPlayfabData()
|
||||
{
|
||||
return new EventOfferJourneyPlayfabData(_eventId, _progress, _ticketCount, _vipLvlOnTrigger, _roundCount, _inflationRate);
|
||||
}
|
||||
|
||||
public override bool AddTicket(int n)
|
||||
{
|
||||
_ticketCount += n;
|
||||
if (_ticketCount < 0)
|
||||
{
|
||||
_ticketCount -= n;
|
||||
return false;
|
||||
}
|
||||
SaveData();
|
||||
// GContext.container.Resolve<FishingEventData>().SaveTransitionData(_eventId, _ticketCount);
|
||||
GContext.Publish(new EventTicketUpdate());
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnBuySuccess()
|
||||
{
|
||||
if (_ctx == null)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Trying to buy pack but there is no context.");
|
||||
return;
|
||||
}
|
||||
if (_progress >= _ctx.GetRewardList(_vipLvlOnTrigger).Count)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Trying to buy pack no.{_progress} but there are only {_ctx.GetRewardList(_vipLvlOnTrigger).Count} rewards.");
|
||||
return;
|
||||
}
|
||||
_progress++;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public override void ProgressUp()
|
||||
{
|
||||
if (_ctx == null)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Trying to progress pack but there is no context.");
|
||||
return;
|
||||
}
|
||||
_progress++;
|
||||
if (_progress >= _ctx.GetRewardList(_vipLvlOnTrigger).Count)
|
||||
{
|
||||
_progress = 0;
|
||||
_roundCount++;
|
||||
Debug.Log($"[EventOfferJourney] Update vip level from {_vipLvlOnTrigger}");
|
||||
_vipLvlOnTrigger = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
Debug.Log($"[EventOfferJourney] to {_vipLvlOnTrigger}");
|
||||
}
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public override void SaveData()
|
||||
{
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public override bool LoadDataFromPlayfab()
|
||||
{
|
||||
var pfData = EventOfferJourneyPlayfabData.Load();
|
||||
if (pfData == null)
|
||||
return false;
|
||||
_eventId = pfData.EventId;
|
||||
_inflationRate = pfData.InflationRate;
|
||||
_progress = pfData.Progress;
|
||||
_ticketCount = pfData.TicketCount;
|
||||
_vipLvlOnTrigger = pfData.VipLvlOnTrigger;
|
||||
_roundCount = pfData.RoundCount;
|
||||
_ctx = EventOfferJourneyTableContext.CreateNew(_eventId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override EventOfferJourneyEntranceData ToEntranceData()
|
||||
{
|
||||
if (_ctx == null)
|
||||
{
|
||||
return new EventOfferJourneyEntranceData(false, TimeSpan.Zero, "", false);
|
||||
}
|
||||
(var startTime, var endTime) = _ctx.GetEventStartTimeAndEndTime();
|
||||
var isActive = ZZTimeHelper.UtcNow() >= startTime && ZZTimeHelper.UtcNow() < endTime && _roundCount < _ctx.MaxRoundCount;
|
||||
var remainingTime = endTime - ZZTimeHelper.UtcNow();
|
||||
var iconUrl = _ctx.EntranceIconUrl;
|
||||
var cost = _ctx.GetTicketCostByProgress(_progress, _vipLvlOnTrigger);
|
||||
var doNeedRedPoint = cost > 0 && cost <= _ticketCount;
|
||||
var res = new EventOfferJourneyEntranceData(isActive, remainingTime, iconUrl, doNeedRedPoint);
|
||||
return res;
|
||||
}
|
||||
|
||||
public override List<EventOfferJourneyBlockData> GetRewardList()
|
||||
{
|
||||
if (_ctx == null)
|
||||
return new List<EventOfferJourneyBlockData>();
|
||||
return _ctx.GetRewardList(_vipLvlOnTrigger);
|
||||
}
|
||||
|
||||
public override ITimerContext ToTimerData(Action OnExpire)
|
||||
{
|
||||
if (_ctx == null)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Trying to get timer data but there is no context.");
|
||||
return new TimerContext();
|
||||
}
|
||||
var res = new TimerContext
|
||||
{
|
||||
StartTime = _ctx.GetEventStartTimeAndEndTime().Item1,
|
||||
ExpiryTime = _ctx.GetEventStartTimeAndEndTime().Item2,
|
||||
OnExpire = OnExpire
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
public override EventOfferJourneyUiData GetUiData()
|
||||
{
|
||||
return _ctx.GetUiData();
|
||||
}
|
||||
|
||||
public override int[] GetTicketRewardArray()
|
||||
{
|
||||
return _ctx.GetTicketRewardArray();
|
||||
}
|
||||
|
||||
public override string GetTicketName()
|
||||
{
|
||||
return _ctx.GetTicketName();
|
||||
}
|
||||
|
||||
public override void OnRaid(int multiplier = 1)
|
||||
{
|
||||
int rewardCount = _ctx.GetTicketRewardArray()[0] * multiplier;
|
||||
GContext.Publish(new game.TargetAddData(_ctx.GetTicketId(), TicketCount, rewardCount));
|
||||
AddTicket(rewardCount);
|
||||
}
|
||||
|
||||
public override void OnBomb(int multiplier = 1)
|
||||
{
|
||||
int rewardCount = _ctx.GetTicketRewardArray()[1] * multiplier;
|
||||
GContext.Publish(new game.TargetAddData(_ctx.GetTicketId(), TicketCount, rewardCount));
|
||||
AddTicket(rewardCount);
|
||||
}
|
||||
|
||||
public override int GetStartPosParam()
|
||||
{
|
||||
return _ctx.GetStartPosParam();
|
||||
}
|
||||
}
|
||||
public abstract class AEventOfferJourneyData : IEventOfferJourneyData
|
||||
{
|
||||
public abstract int Progress { get; }
|
||||
public abstract int TicketCount { get; }
|
||||
public abstract int EventId { get; }
|
||||
public abstract float InflationRate { get; }
|
||||
public abstract ShopBuyType BuyType { get; }
|
||||
public string RedPointKey => "event_offer_journey.entrance";
|
||||
public abstract bool AddTicket(int n);
|
||||
public abstract List<EventOfferJourneyBlockData> GetRewardList();
|
||||
public abstract int GetStartPosParam();
|
||||
public abstract string GetTicketName();
|
||||
public abstract int[] GetTicketRewardArray();
|
||||
public abstract EventOfferJourneyUiData GetUiData();
|
||||
public abstract bool LoadDataFromPlayfab();
|
||||
public abstract void OnBomb(int multiplier = 1);
|
||||
public abstract void OnBuySuccess();
|
||||
public abstract void OnRaid(int multiplier = 1);
|
||||
public abstract void ProgressUp();
|
||||
public abstract void SaveData();
|
||||
public abstract EventOfferJourneyEntranceData ToEntranceData();
|
||||
public abstract ITimerContext ToTimerData(Action OnExpire);
|
||||
}
|
||||
|
||||
public interface IEventOfferJourneyData
|
||||
{
|
||||
public int Progress { get; }
|
||||
public int TicketCount { get; }
|
||||
public int EventId { get; }
|
||||
public float InflationRate { get; }
|
||||
public ShopBuyType BuyType { get; }
|
||||
public bool AddTicket(int n);
|
||||
public bool LoadDataFromPlayfab();
|
||||
public void SaveData();
|
||||
public void ProgressUp();
|
||||
public void OnBuySuccess();
|
||||
public List<EventOfferJourneyBlockData> GetRewardList();
|
||||
public EventOfferJourneyEntranceData ToEntranceData();
|
||||
public EventOfferJourneyUiData GetUiData();
|
||||
public ITimerContext ToTimerData(Action OnExpire);
|
||||
public int[] GetTicketRewardArray();
|
||||
public string GetTicketName();
|
||||
public void OnRaid(int multiplier = 1);
|
||||
public void OnBomb(int multiplier = 1);
|
||||
public int GetStartPosParam();
|
||||
public string RedPointKey { get; }
|
||||
}
|
||||
|
||||
public class EventOfferJourneyBlockData
|
||||
{
|
||||
public enum EType { IAP, Ticket }
|
||||
public EType Type;
|
||||
public int Price, DropId;
|
||||
private EventOfferJourneyBlockData() { }
|
||||
|
||||
public static EventOfferJourneyBlockData CreateIapData(int iapId, int dropId)
|
||||
{
|
||||
var data = new EventOfferJourneyBlockData();
|
||||
data.Type = EType.IAP;
|
||||
data.Price = iapId;
|
||||
data.DropId = dropId;
|
||||
return data;
|
||||
}
|
||||
|
||||
public static EventOfferJourneyBlockData CreateTicketData(int ticketCost, int dropId)
|
||||
{
|
||||
var data = new EventOfferJourneyBlockData();
|
||||
data.Type = EType.Ticket;
|
||||
data.Price = ticketCost;
|
||||
data.DropId = dropId;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventOfferJourneyPlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int EventId, Progress, TicketCount, VipLvlOnTrigger, RoundCount;
|
||||
public float InflationRate;
|
||||
private const string Key = "EventOfferJourneyData";
|
||||
private const int TokenCount = 6;
|
||||
private const char Splitter = '|', Comma = ',';
|
||||
|
||||
public EventOfferJourneyPlayfabData(int eventId, int progress, int ticketCount, int vipLvlOnTrigger, int roundCount, float inflationRate)
|
||||
{
|
||||
EventId = eventId;
|
||||
Progress = progress;
|
||||
TicketCount = ticketCount;
|
||||
VipLvlOnTrigger = vipLvlOnTrigger;
|
||||
RoundCount = roundCount;
|
||||
InflationRate = inflationRate;
|
||||
}
|
||||
|
||||
private string Serialize()
|
||||
{
|
||||
var res = new StringBuilder();
|
||||
res.Append(EventId).Append(Splitter)
|
||||
.Append(Progress).Append(Splitter)
|
||||
.Append(TicketCount).Append(Splitter)
|
||||
.Append(VipLvlOnTrigger).Append(Splitter)
|
||||
.Append(RoundCount).Append(Splitter)
|
||||
.Append(InflationRate);
|
||||
return res.ToString();
|
||||
}
|
||||
|
||||
private static EventOfferJourneyPlayfabData Deserialize(string s)
|
||||
{
|
||||
if (s == null || s == "")
|
||||
return null;
|
||||
var tokens = s.Trim(Splitter).Split(Splitter);
|
||||
if (tokens.Length != TokenCount && tokens.Length != TokenCount - 1)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Wrong amount of parameters. Expect {TokenCount}, but got {tokens.Length} in \"{s}\".");
|
||||
return null;
|
||||
}
|
||||
|
||||
var eventId = int.Parse(tokens[0]);
|
||||
var progress = int.Parse(tokens[1]);
|
||||
var ticketCount = int.Parse(tokens[2]);
|
||||
var vipLvlOnTrigger = int.Parse(tokens[3]);
|
||||
var roundCount = int.Parse(tokens[4]);
|
||||
float inflationRate = 0;
|
||||
if (tokens.Length == TokenCount)
|
||||
{
|
||||
inflationRate = float.Parse(tokens[5]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Old data detected, missing inflation rate. Setting to 0.");
|
||||
}
|
||||
return new EventOfferJourneyPlayfabData(eventId, progress, ticketCount, vipLvlOnTrigger, roundCount, inflationRate);
|
||||
}
|
||||
|
||||
public static EventOfferJourneyPlayfabData Load()
|
||||
{
|
||||
var s = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>().Read(Key).ValueOrThrow();
|
||||
return Deserialize(s);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
saveStore.Write(Key, s).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
public class EventOfferJourneyEntranceData
|
||||
{
|
||||
public bool IsActive { get; }
|
||||
public TimeSpan RemainingTime { get; }
|
||||
public string IconUrl { get; }
|
||||
public bool DoNeedRedPoint { get; }
|
||||
public EventOfferJourneyEntranceData(bool isActive, TimeSpan remainingTime, string iconUrl, bool doNeedRedPoint)
|
||||
{
|
||||
IsActive = isActive;
|
||||
RemainingTime = remainingTime;
|
||||
IconUrl = iconUrl;
|
||||
DoNeedRedPoint = doNeedRedPoint;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventOfferJourneyUiData
|
||||
{
|
||||
public string PanelUrl { get; set; }
|
||||
public string InfoPanelUrl { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using GameCore;
|
||||
using UnityEngine;
|
||||
using asap.core;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class EventOfferJourneyLvlTriggerData : AEventOfferJourneyData
|
||||
{
|
||||
private int _progress, _ticketCount, _roundCount;
|
||||
private EventOfferJourneyLvlTriggerTableContext _ctx = new EventOfferJourneyLvlTriggerTableContext();
|
||||
public override int EventId => _ctx.TriggerPackId;
|
||||
public override int Progress => _progress;
|
||||
public override int TicketCount => _ticketCount;
|
||||
public override float InflationRate => 0;
|
||||
|
||||
public override ShopBuyType BuyType => ShopBuyType.TriggerPack;
|
||||
public bool IsActive => _ctx.IsEventActive && _roundCount < _ctx.MaxRoundCount;
|
||||
// private int VipLvlOnTrigger => _ctx.VipLvlOnTrigger;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
_progress = 0;
|
||||
_ticketCount = 0;
|
||||
_roundCount = 0;
|
||||
}
|
||||
|
||||
public override bool AddTicket(int n)
|
||||
{
|
||||
_ticketCount += n;
|
||||
if (_ticketCount < 0)
|
||||
{
|
||||
_ticketCount -= n;
|
||||
return false;
|
||||
}
|
||||
SaveData();
|
||||
// GContext.container.Resolve<FishingEventData>().SaveTransitionData(_eventId, _ticketCount);
|
||||
GContext.Publish(new EventTicketUpdate());
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnBuySuccess()
|
||||
{
|
||||
if (_progress >= _ctx.GetRewardList().Count)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Trying to buy pack no.{_progress} but there are only {_ctx.GetRewardList().Count} rewards.");
|
||||
return;
|
||||
}
|
||||
_progress++;
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public override void ProgressUp()
|
||||
{
|
||||
_progress++;
|
||||
if (_progress >= _ctx.GetRewardList().Count)
|
||||
{
|
||||
_progress = 0;
|
||||
_roundCount++;
|
||||
}
|
||||
SaveData();
|
||||
}
|
||||
|
||||
public override void SaveData()
|
||||
{
|
||||
ToPlayfabData().Save();
|
||||
}
|
||||
|
||||
public override bool LoadDataFromPlayfab()
|
||||
{
|
||||
var pfData = EventOfferJourneyLvlTriggerPlayfabData.Load();
|
||||
if (pfData == null)
|
||||
return false;
|
||||
_progress = pfData.Progress;
|
||||
_ticketCount = pfData.TicketCount;
|
||||
_roundCount = pfData.RoundCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
private EventOfferJourneyLvlTriggerPlayfabData ToPlayfabData()
|
||||
{
|
||||
return new EventOfferJourneyLvlTriggerPlayfabData(_progress, _ticketCount, _roundCount);
|
||||
}
|
||||
|
||||
public override EventOfferJourneyEntranceData ToEntranceData()
|
||||
{
|
||||
if (!IsActive)
|
||||
{
|
||||
return new EventOfferJourneyEntranceData(false, TimeSpan.Zero, "", false);
|
||||
}
|
||||
TimeSpan remainingTime = _ctx.GetEventStartTimeAndEndTime().Item2 - ZZTimeHelper.UtcNow();
|
||||
// Debug.Log($"[EventOfferJourney] Remaining time: {remainingTime.TotalSeconds}s.");
|
||||
var isActive = IsActive && remainingTime.TotalSeconds > 0;
|
||||
// Debug.Log($"[EventOfferJourney] First Is active: {IsActive}.");
|
||||
// Debug.Log($"[EventOfferJourney] Total Is active: {isActive}.");
|
||||
var iconUrl = _ctx.EntranceIconUrl;
|
||||
// Debug.Log($"[EventOfferJourney] Icon URL: {iconUrl}.");
|
||||
var cost = _ctx.GetTicketCostByProgress(_progress);
|
||||
// Debug.Log($"[EventOfferJourney] Cost: {cost}.");
|
||||
var doNeedRedPoint = cost > 0 && cost <= _ticketCount;
|
||||
// Debug.Log($"[EventOfferJourney] Do need red point: {doNeedRedPoint}.");
|
||||
var res = new EventOfferJourneyEntranceData(isActive, remainingTime, iconUrl, doNeedRedPoint);
|
||||
return res;
|
||||
}
|
||||
|
||||
public override List<EventOfferJourneyBlockData> GetRewardList()
|
||||
{
|
||||
return _ctx.GetRewardList();
|
||||
}
|
||||
|
||||
public override ITimerContext ToTimerData(Action OnExpire)
|
||||
{
|
||||
var res = new TimerContext
|
||||
{
|
||||
StartTime = _ctx.GetEventStartTimeAndEndTime().Item1,
|
||||
ExpiryTime = _ctx.GetEventStartTimeAndEndTime().Item2,
|
||||
OnExpire = OnExpire
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
public override EventOfferJourneyUiData GetUiData()
|
||||
{
|
||||
return _ctx.GetUiData();
|
||||
}
|
||||
public override int[] GetTicketRewardArray()
|
||||
{
|
||||
return _ctx.GetTicketRewardArray();
|
||||
}
|
||||
|
||||
public override string GetTicketName()
|
||||
{
|
||||
return _ctx.GetTicketName();
|
||||
}
|
||||
|
||||
public override void OnRaid(int multiplier = 1)
|
||||
{
|
||||
int rewardCount = _ctx.GetTicketRewardArray()[0] * multiplier;
|
||||
GContext.Publish(new game.TargetAddData(_ctx.GetTicketId(), TicketCount, rewardCount));
|
||||
AddTicket(rewardCount);
|
||||
}
|
||||
|
||||
public override void OnBomb(int multiplier = 1)
|
||||
{
|
||||
int rewardCount = _ctx.GetTicketRewardArray()[1] * multiplier;
|
||||
GContext.Publish(new game.TargetAddData(_ctx.GetTicketId(), TicketCount, rewardCount));
|
||||
AddTicket(rewardCount);
|
||||
}
|
||||
|
||||
public override int GetStartPosParam()
|
||||
{
|
||||
return _ctx.GetStartPosParam();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class EventOfferJourneyLvlTriggerPlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int Progress, TicketCount, RoundCount;
|
||||
private const string Key = "EventOfferJourneyLvlTriggerData";
|
||||
private const int TokenCount = 3;
|
||||
private const char Splitter = '|', Comma = ',';
|
||||
|
||||
public EventOfferJourneyLvlTriggerPlayfabData(int progress, int ticketCount, int roundCount)
|
||||
{
|
||||
Progress = progress;
|
||||
TicketCount = ticketCount;
|
||||
RoundCount = roundCount;
|
||||
}
|
||||
|
||||
private string Serialize()
|
||||
{
|
||||
var res = new StringBuilder();
|
||||
res.Append(Progress).Append(Splitter)
|
||||
.Append(TicketCount).Append(Splitter)
|
||||
.Append(RoundCount).Append(Splitter);
|
||||
return res.ToString();
|
||||
}
|
||||
|
||||
private static EventOfferJourneyLvlTriggerPlayfabData Deserialize(string s)
|
||||
{
|
||||
if (s == null || s == "")
|
||||
return null;
|
||||
var tokens = s.Trim(Splitter).Split(Splitter);
|
||||
if (tokens.Length != TokenCount)
|
||||
{
|
||||
Debug.LogError($"[EventOfferJourney]Wrong amount of parameters. Expect {TokenCount}, but got {tokens.Length} in \"{s}\".");
|
||||
return null;
|
||||
}
|
||||
var progress = int.Parse(tokens[0]);
|
||||
var ticketCount = int.Parse(tokens[1]);
|
||||
var roundCount = int.Parse(tokens[2]);
|
||||
return new EventOfferJourneyLvlTriggerPlayfabData(progress, ticketCount, roundCount);
|
||||
}
|
||||
|
||||
public static EventOfferJourneyLvlTriggerPlayfabData Load()
|
||||
{
|
||||
var s = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>().Read(Key).ValueOrThrow();
|
||||
return Deserialize(s);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
saveStore.Write(Key, s).RequireAccepted();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,741 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
public class EventLuckMagicModel : IHasChainPack, ILuckyTaskGetData
|
||||
{
|
||||
private int _eventId, _ticketCount, _chainProgress, _roundCount;
|
||||
|
||||
/// <summary>
|
||||
/// Position of selection GameObject, or null if hidden.
|
||||
/// </summary>
|
||||
public Vector2? SelectionState;
|
||||
private readonly List<EventLuckMagicRing> _rings = new List<EventLuckMagicRing>();
|
||||
public EventLuckMagicRing CurrentRing { get; set; }
|
||||
public EventLuckMagicRing OuterRing => _rings[0];
|
||||
public EventLuckMagicRing MiddleRing => _rings[1];
|
||||
public EventLuckMagicRing InnerRing => _rings[2];
|
||||
public EventLuckMagicHigherRingData HigherRingData;
|
||||
public Dictionary<EEventLuckMagicRingType, EventLuckMagicRing> RingMap;
|
||||
public int TicketCount => _ticketCount;
|
||||
public Dictionary<int, int> TaskProgress;
|
||||
public const int TaskProgressCount = 3;
|
||||
public bool IsGameDepleted => _rings.All(x => x.IsDepleted);
|
||||
public bool DoesSkipAnimation = false;
|
||||
private DateTime _expiryTime, _startTime;
|
||||
public TimeSpan RemainingTime => _expiryTime - ZZTimeHelper.UtcNow();
|
||||
public bool IsActive => RemainingTime.TotalSeconds > 0;
|
||||
public int EventId => _eventId;
|
||||
public int RoundCount => _roundCount;
|
||||
public DateTime ExpiryTime => _expiryTime;
|
||||
|
||||
public EventLuckMagicParamsCtrl ParamsCtrl;
|
||||
|
||||
public void InitNew(int eventId)
|
||||
{
|
||||
float inflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
_eventId = eventId;
|
||||
_rings.Clear();
|
||||
EventLuckMagicAct.TableContext.GetRingsInfo(out int[] costs, out int[] roadSignIds);
|
||||
for (int i = 0; i < costs.Length; i++)
|
||||
_rings.Add(EventLuckMagicRing.CreateEmpty(costs[i], roadSignIds[i], (EEventLuckMagicRingType)(i + 1)));
|
||||
EventLuckMagicAct.TableContext.GetBlocksInfo(out var infoList, inflationRate);
|
||||
RingMap = new Dictionary<EEventLuckMagicRingType, EventLuckMagicRing>()
|
||||
{
|
||||
{EEventLuckMagicRingType.OuterRing, OuterRing},
|
||||
{EEventLuckMagicRingType.MiddleRing, MiddleRing},
|
||||
{EEventLuckMagicRingType.InnerRing, InnerRing}
|
||||
};
|
||||
foreach (var info in infoList)
|
||||
{
|
||||
RingMap[info.RingType].AddBlock(new EventLuckMagicBlock(info, RingMap[info.RingType]));
|
||||
}
|
||||
RingMap[EEventLuckMagicRingType.OuterRing].ImportBitWiseBlockState(0);
|
||||
RingMap[EEventLuckMagicRingType.MiddleRing].ImportBitWiseBlockState(0);
|
||||
RingMap[EEventLuckMagicRingType.InnerRing].ImportBitWiseBlockState(0);
|
||||
|
||||
HigherRingData = EventLuckMagicHigherRingData.CreateEmpty();
|
||||
LinkRings();
|
||||
|
||||
_ticketCount = EventLuckMagicAct.TableContext.GetWelcomeGift();
|
||||
_chainProgress = 0;
|
||||
|
||||
TaskProgress = GetTaskProgress(inflationRate);
|
||||
CurrentRing = _rings.FirstOrDefault(r => !r.IsDepleted) ?? _rings.Last();
|
||||
SelectionState = null;
|
||||
DoesSkipAnimation = false;
|
||||
(_startTime, _expiryTime) = EventLuckMagicAct.TableContext.GetEventStartTimeAndEndTime();
|
||||
_roundCount = 0;
|
||||
|
||||
_taskData?.Dispose();
|
||||
_dicTasks = new Dictionary<int, int>();
|
||||
_taskData = new LuckyTaskData(this);
|
||||
_taskData.InitTasks();
|
||||
}
|
||||
|
||||
public void Load(EventLuckMagicPlayfabData pfData, EventLuckMagicPlayerPreferenceData ppData)
|
||||
{
|
||||
_eventId = pfData.EventId;
|
||||
_dicTasks = pfData.DicTasks;
|
||||
_taskData = new LuckyTaskData(this);
|
||||
float inflationRate = _dicTasks.GetValueOrDefault(-3, 0) / 100f;
|
||||
EventLuckMagicAct.TableContext.GetRingsInfo(out int[] costs, out int[] roadSignIds);
|
||||
for (int i = 0; i < costs.Length; i++)
|
||||
_rings.Add(EventLuckMagicRing.CreateEmpty(costs[i], roadSignIds[i], (EEventLuckMagicRingType)(i + 1)));
|
||||
EventLuckMagicAct.TableContext.GetBlocksInfo(out var infoList, inflationRate);
|
||||
RingMap = new Dictionary<EEventLuckMagicRingType, EventLuckMagicRing>()
|
||||
{
|
||||
{EEventLuckMagicRingType.OuterRing, OuterRing},
|
||||
{EEventLuckMagicRingType.MiddleRing, MiddleRing},
|
||||
{EEventLuckMagicRingType.InnerRing, InnerRing}
|
||||
};
|
||||
foreach (var info in infoList)
|
||||
{
|
||||
RingMap[info.RingType].AddBlock(new EventLuckMagicBlock(info, RingMap[info.RingType]));
|
||||
}
|
||||
RingMap[EEventLuckMagicRingType.OuterRing].ImportBitWiseBlockState(pfData.OuterState);
|
||||
RingMap[EEventLuckMagicRingType.MiddleRing].ImportBitWiseBlockState(pfData.MiddleState);
|
||||
RingMap[EEventLuckMagicRingType.InnerRing].ImportBitWiseBlockState(pfData.InnerState);
|
||||
|
||||
HigherRingData = EventLuckMagicHigherRingData.Create(RingMap[pfData.HigherRingType], pfData.HigherRingExpireTime.Value);
|
||||
LinkRings();
|
||||
|
||||
_ticketCount = pfData.TicketCount;
|
||||
_chainProgress = pfData.ChainProgress;
|
||||
_roundCount = pfData.RoundCount;
|
||||
|
||||
TaskProgress = GetTaskProgress(inflationRate);
|
||||
CurrentRing = _rings.FirstOrDefault(r => !r.IsDepleted) ?? _rings.Last();
|
||||
ppData ??= EventLuckMagicPlayerPreferenceData.CreateEmpty();
|
||||
SelectionState = ppData.SelectionPosition;
|
||||
DoesSkipAnimation = ppData.DoesSkipAnimation;
|
||||
(_startTime, _expiryTime) = EventLuckMagicAct.TableContext.GetEventStartTimeAndEndTime();
|
||||
SetRedPoint();
|
||||
}
|
||||
void SetRedPoint()
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState(EventLuckMagicEntranceButton.RedPointKey,
|
||||
TicketCount > 0, TicketCount + _taskData.RedCount);
|
||||
}
|
||||
public void AddTicket(int num)
|
||||
{
|
||||
if (_ticketCount + num < 0)
|
||||
{
|
||||
Debug.Log($"[EventLuckMagic] Try to add {num} ticket(s) failed. Having {_ticketCount} ticket(s) now.");
|
||||
return;
|
||||
}
|
||||
_ticketCount += num;
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(_eventId, _ticketCount);
|
||||
ToPfData().Save();
|
||||
EventLuckMagicAct.EventAggregator.Publish(new EventLuckMagicTicketChange());
|
||||
SetRedPoint();
|
||||
}
|
||||
|
||||
public void LinkRings()
|
||||
{
|
||||
for (int i = 0; i < _rings.Count - 1; i++)
|
||||
_rings[i].HigherRing = _rings[i + 1];
|
||||
CurrentRing = _rings.FirstOrDefault(r => !r.IsDepleted);
|
||||
if (CurrentRing == null)
|
||||
{
|
||||
Debug.Log($"[EventLuckMagic] Cannot find any available ring. Seems that all rings are depleted.");
|
||||
return;
|
||||
}
|
||||
var r = CurrentRing;
|
||||
// The Higher ring of r is the next ring that is not depleted.
|
||||
while (r != null && r.HigherRing != null)
|
||||
{
|
||||
while (r.HigherRing != null && r.HigherRing.IsDepleted)
|
||||
{
|
||||
r.HigherRing = r.HigherRing.HigherRing;
|
||||
}
|
||||
r = r.HigherRing;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateTaskProgress(int targetId)
|
||||
{
|
||||
TaskProgress[targetId]++;
|
||||
// Debug.Log($"[EventLuckMagic] Task progress of {targetId} is {TaskProgress[targetId]}.");
|
||||
ToPfData().Save();
|
||||
}
|
||||
|
||||
public bool IsTaskCompleted(int targetId)
|
||||
{
|
||||
return TaskProgress[targetId] >= TaskProgressCount;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
for (int i = 0; i < _rings.Count; i++)
|
||||
_rings[i].Reset();
|
||||
HigherRingData.SetEmpty();
|
||||
LinkRings();
|
||||
HigherRingData.PointTo(CurrentRing.HigherRing);
|
||||
HigherRingData.ExpireTime = System.DateTime.MinValue;
|
||||
int inflationRate = DicTasks.GetValueOrDefault(-3, 0);
|
||||
float inflate = inflationRate / 100f;
|
||||
|
||||
TaskProgress = EventLuckMagicAct.TableContext.GetTaskInfo(GContext.container.Resolve<EventLuckMagicModel>().RoundCount, inflate)
|
||||
.Keys.ToDictionary(x => x, x => 0);
|
||||
CurrentRing = _rings.FirstOrDefault(r => !r.IsDepleted) ?? _rings.Last();
|
||||
|
||||
_taskData.RefreshTasks();
|
||||
|
||||
ToPfData().Save();
|
||||
}
|
||||
|
||||
public void SetChainProgress(int p)
|
||||
{
|
||||
Debug.Log($"[EventLuckMagic]SetProgress {p}");
|
||||
_chainProgress = p;
|
||||
}
|
||||
|
||||
public int GetChainProgress()
|
||||
{
|
||||
return _chainProgress;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
ToPfData().Save();
|
||||
}
|
||||
|
||||
public EventLuckMagicPlayerPreferenceData ToPpData()
|
||||
{
|
||||
return new EventLuckMagicPlayerPreferenceData
|
||||
{
|
||||
SelectionPosition = SelectionState,
|
||||
DoesSkipAnimation = DoesSkipAnimation
|
||||
};
|
||||
}
|
||||
|
||||
public EventLuckMagicPlayfabData ToPfData()
|
||||
{
|
||||
var expireTime = HigherRingData.DoesExist ? HigherRingData.ExpireTime : DateTime.MinValue;
|
||||
// var expireTime = HigherRingData.DoesExist ? HigherRingData.ExpireTime : ZZTimeHelper.UtcNow().AddSeconds(-1);
|
||||
var ringType = HigherRingData.DoesExist ? HigherRingData.Ring.RingType : EEventLuckMagicRingType.OuterRing;
|
||||
return new EventLuckMagicPlayfabData
|
||||
{
|
||||
EventId = _eventId,
|
||||
TicketCount = TicketCount,
|
||||
ChainProgress = _chainProgress,
|
||||
InnerState = InnerRing.GetBitWiseBlockState(),
|
||||
MiddleState = MiddleRing.GetBitWiseBlockState(),
|
||||
OuterState = OuterRing.GetBitWiseBlockState(),
|
||||
HigherRingExpireTime = expireTime,
|
||||
HigherRingType = ringType,
|
||||
RoundCount = _roundCount,
|
||||
DicTasks = _dicTasks
|
||||
};
|
||||
}
|
||||
|
||||
private Dictionary<int, int> GetTaskProgress(float inflate)
|
||||
{
|
||||
Debug.Log($"[EventLuckMagic] GetTaskProgress with inflation {inflate}");
|
||||
var taskInfo = EventLuckMagicAct.TableContext.GetTaskInfo(RoundCount, inflate);
|
||||
var res = taskInfo.ToDictionary(
|
||||
kv => kv.Key,
|
||||
kv => _rings.Sum(ring => ring.Blocks.Count(x => x.Reward.id == kv.Key && x.IsTaken))
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
public EventLuckMagicEntranceData ToEntranceData()
|
||||
{
|
||||
return new EventLuckMagicEntranceData
|
||||
{
|
||||
TicketCount = _ticketCount,
|
||||
ExpiryTime = _expiryTime,
|
||||
Icon = EventLuckMagicAct.TableContext.GetIcon(),
|
||||
StartTime = _startTime,
|
||||
};
|
||||
}
|
||||
|
||||
public GenericChainPackData<EventLuckMagicModel> ToChainPackData()
|
||||
{
|
||||
return GenericChainPackData<EventLuckMagicModel>.Create(
|
||||
this, EventLuckMagicAct.TableContext.GetChainPackInfo());
|
||||
}
|
||||
|
||||
public void AddRoundCount()
|
||||
{
|
||||
_roundCount++;
|
||||
Save();
|
||||
}
|
||||
|
||||
public void SaveTaskData()
|
||||
{
|
||||
SetRedPoint();
|
||||
ToPfData().Save();
|
||||
}
|
||||
|
||||
|
||||
#region Lucky Task
|
||||
private Dictionary<int, int> _dicTasks;
|
||||
private LuckyTaskData _taskData;
|
||||
public string LuckyTaskRedPointKey => "eventluckmagic.entrance.luckytask";
|
||||
public Dictionary<int, int> DicTasks { get => _dicTasks; set => _dicTasks = value; }
|
||||
public string TaskPanelName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DicTasks.GetValueOrDefault(-4, 0) == 1)
|
||||
{
|
||||
return "EventLuckyMagicADTaskPopupPanel";
|
||||
}
|
||||
return "EventLuckyMagicTaskPopupPanel";
|
||||
}
|
||||
}
|
||||
public bool OnGetTaskReward(int id)
|
||||
{
|
||||
return _taskData.OnGetTaskReward(id);
|
||||
}
|
||||
|
||||
public int GetLuckyPackID()
|
||||
{
|
||||
return EventLuckMagicAct.TableContext.GetPackId();
|
||||
}
|
||||
|
||||
public void DisposeLuckyTaskSubscription()
|
||||
{
|
||||
_taskData.Dispose();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class EventLuckMagicRing
|
||||
{
|
||||
public readonly int Cost, RoadSignId;
|
||||
public List<EventLuckMagicBlock> Blocks;
|
||||
public bool IsDepleted => Blocks.All(x => x.IsTaken || EventLuckMagicAct.TableContext.IsRewardRoadSign(x.Reward.id, out _, out _, out _));
|
||||
public readonly EEventLuckMagicRingType RingType;
|
||||
public EventLuckMagicRing HigherRing { get; set; }
|
||||
public int[] CornerIndices = new int[4];
|
||||
|
||||
private int BlockCount => Blocks.Count;
|
||||
|
||||
public int AvailableBlockCount => Blocks.Count(x => !x.IsTaken);
|
||||
|
||||
private EventLuckMagicRing(int cost, int roadSignId, EEventLuckMagicRingType ringType)
|
||||
{
|
||||
Blocks = new List<EventLuckMagicBlock>();
|
||||
Cost = cost;
|
||||
RoadSignId = roadSignId;
|
||||
RingType = ringType;
|
||||
HigherRing = null;
|
||||
}
|
||||
|
||||
public static EventLuckMagicRing CreateEmpty(int cost, int roadSignId, EEventLuckMagicRingType ringType)
|
||||
{
|
||||
return new EventLuckMagicRing(cost, roadSignId, ringType);
|
||||
}
|
||||
|
||||
public void AddBlock(EventLuckMagicBlock block)
|
||||
{
|
||||
Blocks.Add(block);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Blocks.ForEach(x => x.Reset());
|
||||
}
|
||||
|
||||
public void GetLoopAnimationInfo(int targetIdx, int extraLoopCount, out int startIdx, out int totalMovementCount)
|
||||
{
|
||||
int idx = 0, gap = BlockCount / 4;
|
||||
startIdx = 0;
|
||||
while (idx < BlockCount)
|
||||
{
|
||||
idx += gap;
|
||||
if (GetShortestDistance(idx, targetIdx) > GetShortestDistance(startIdx, targetIdx))
|
||||
startIdx = idx;
|
||||
}
|
||||
totalMovementCount = GetClockWiseDistance(startIdx, targetIdx) + extraLoopCount * BlockCount;
|
||||
}
|
||||
|
||||
private int GetClockWiseDistance(int startIdx, int endIdx)
|
||||
{
|
||||
Assert.IsTrue(startIdx >= 0 && startIdx < BlockCount && endIdx >= 0 && endIdx < BlockCount);
|
||||
return (endIdx - startIdx + BlockCount) % BlockCount;
|
||||
}
|
||||
|
||||
private int GetShortestDistance(int startIdx, int endIdx)
|
||||
{
|
||||
int d = Mathf.Abs(startIdx - endIdx);
|
||||
d = d > BlockCount / 2 ? BlockCount - d : d;
|
||||
return d;
|
||||
}
|
||||
|
||||
public int GetBitWiseBlockState()
|
||||
{
|
||||
var res = 0;
|
||||
for (int i = 0; i < BlockCount; i++)
|
||||
{
|
||||
if (Blocks[i].IsTaken)
|
||||
res |= 1 << i;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void ImportBitWiseBlockState(int state)
|
||||
{
|
||||
for (int i = 0; i < BlockCount; i++)
|
||||
{
|
||||
if ((state & (1 << i)) != 0)
|
||||
Blocks[i].Take();
|
||||
else
|
||||
Blocks[i].Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class EventLuckMagicBlock
|
||||
{
|
||||
public int TableId;
|
||||
public readonly ItemData Reward;
|
||||
private bool _isTaken;
|
||||
public bool IsTaken
|
||||
{
|
||||
get
|
||||
{
|
||||
if (EventLuckMagicAct.TableContext.IsRewardRoadSign(Reward.id, out _, out _, out _))
|
||||
return ParentRing == null || ParentRing.HigherRing == null || ParentRing.HigherRing.IsDepleted;
|
||||
return _isTaken;
|
||||
}
|
||||
}
|
||||
|
||||
private int _weight;
|
||||
public int Weight
|
||||
{
|
||||
get
|
||||
{
|
||||
if (EventLuckMagicAct.TableContext.IsRewardRoadSign(Reward.id, out _, out _, out var weightConfig))
|
||||
{
|
||||
if (weightConfig.Length != 2)
|
||||
return _weight;
|
||||
return ParentRing.AvailableBlockCount <= weightConfig[0] ? weightConfig[1] : _weight;
|
||||
}
|
||||
return _weight;
|
||||
}
|
||||
set
|
||||
{
|
||||
_weight = value;
|
||||
}
|
||||
}
|
||||
public EEventLuckMagicBlockType BlockType;
|
||||
/// <summary>
|
||||
/// The ring this block belongs to.
|
||||
/// </summary>
|
||||
public EventLuckMagicRing ParentRing { get; }
|
||||
|
||||
public EventLuckMagicBlock(EventLuckMagicBlockInfo info, EventLuckMagicRing parentRing)
|
||||
{
|
||||
Reward = info.Reward;
|
||||
Weight = info.Weight;
|
||||
BlockType = GetBlockType(info.RingType, info.Reward.id);
|
||||
ParentRing = parentRing;
|
||||
TableId = info.TableId;
|
||||
// _isTaken = isTaken;
|
||||
}
|
||||
|
||||
public void Take()
|
||||
{
|
||||
_isTaken = true;
|
||||
}
|
||||
|
||||
private static EEventLuckMagicBlockType GetBlockType(EEventLuckMagicRingType ringType, int rewardId)
|
||||
{
|
||||
if (EventLuckMagicAct.TableContext.IsRewardRoadSign(ringType, rewardId))
|
||||
return EEventLuckMagicBlockType.RoadSign;
|
||||
else
|
||||
return (EEventLuckMagicBlockType)(ringType - 1);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_isTaken = false;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventLuckMagicHigherRingData
|
||||
{
|
||||
public EventLuckMagicRing Ring { get; set; }
|
||||
public System.DateTime ExpireTime;
|
||||
public System.TimeSpan RemainingTime => ExpireTime - ZZTimeHelper.UtcNow();
|
||||
public bool DoesExist => Ring != null && !Ring.IsDepleted && RemainingTime.TotalSeconds > 0;
|
||||
|
||||
private EventLuckMagicHigherRingData() { }
|
||||
|
||||
public void PointTo(EventLuckMagicRing ring)
|
||||
{
|
||||
Ring = ring;
|
||||
}
|
||||
|
||||
public void Activate(int expireDuration)
|
||||
{
|
||||
ExpireTime = ZZTimeHelper.UtcNow().AddSeconds(expireDuration);
|
||||
}
|
||||
|
||||
public void SetEmpty()
|
||||
{
|
||||
Ring = null;
|
||||
ExpireTime = ZZTimeHelper.UtcNow().AddSeconds(-5);
|
||||
}
|
||||
|
||||
public static EventLuckMagicHigherRingData CreateEmpty()
|
||||
{
|
||||
var res = new EventLuckMagicHigherRingData();
|
||||
res.SetEmpty();
|
||||
return res;
|
||||
}
|
||||
|
||||
public static EventLuckMagicHigherRingData Create(EventLuckMagicRing ring, System.DateTime? expireTime)
|
||||
{
|
||||
var res = new EventLuckMagicHigherRingData();
|
||||
res.PointTo(ring);
|
||||
if (expireTime != null)
|
||||
res.ExpireTime = expireTime.Value;
|
||||
else
|
||||
res.ExpireTime = ZZTimeHelper.UtcNow().AddSeconds(-4);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventLuckMagicPanelData
|
||||
{
|
||||
public EventLuckMagicTableContext TableContext;
|
||||
public EventLuckMagicModel Model;
|
||||
public EventLuckMagicSystem System;
|
||||
public EventLuckMagicParamsCtrl ParamsCtrl;
|
||||
public IEventAggregator EventAggregator;
|
||||
public Vector2? SelectionPosition;
|
||||
}
|
||||
|
||||
public class EventLuckMagicSpinParams
|
||||
{
|
||||
public float MaxInterval;
|
||||
public float MinInterval;
|
||||
public int ExtraLoopCount;
|
||||
public AnimationCurve IntervalLerpCurve;
|
||||
}
|
||||
|
||||
public class EventLuckMagicSpinCtx
|
||||
{
|
||||
public EventLuckMagicRing Ring;
|
||||
public int Index;
|
||||
public ItemData Reward;
|
||||
public bool doesHideButton;
|
||||
public int RoundCount;
|
||||
}
|
||||
|
||||
public class RewardFlyToStashRequest
|
||||
{
|
||||
public ItemData Reward;
|
||||
public bool DoesPlayOpen;
|
||||
// public Vector2 Position;
|
||||
// public float SourceIconSize;
|
||||
public RectTransform SourceIconRt;
|
||||
public RectTransform SourceTextRt;
|
||||
public RewardFlyToStashRequest(ItemData reward, RectTransform sourceIconRt,
|
||||
bool doesPlayOpen = true, RectTransform sourceTextRt = null)
|
||||
{
|
||||
Reward = reward;
|
||||
DoesPlayOpen = doesPlayOpen;
|
||||
SourceIconRt = sourceIconRt;
|
||||
SourceTextRt = sourceTextRt;
|
||||
}
|
||||
}
|
||||
|
||||
public class RewardItemNewFlyToStashRequest
|
||||
{
|
||||
public RewardItemNew Source;
|
||||
}
|
||||
|
||||
public class EventLuckMagicPlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int EventId;
|
||||
public int TicketCount;
|
||||
public int ChainProgress;
|
||||
public int InnerState, MiddleState, OuterState;
|
||||
public int RoundCount;
|
||||
public System.DateTime? HigherRingExpireTime;
|
||||
public EEventLuckMagicRingType HigherRingType;
|
||||
public Dictionary<int, int> DicTasks;
|
||||
|
||||
public const string Key = "EventLuckMagicPlayfabData";
|
||||
private const string Splitter = "|", Comma = ",";
|
||||
private const int Count = 11;
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(EventId).Append(Splitter);
|
||||
sb.Append(TicketCount).Append(Splitter);
|
||||
sb.Append(ChainProgress).Append(Splitter);
|
||||
sb.Append(InnerState).Append(Splitter);
|
||||
sb.Append(MiddleState).Append(Splitter);
|
||||
sb.Append(OuterState).Append(Splitter);
|
||||
sb.Append(HigherRingExpireTime).Append(Splitter);
|
||||
sb.Append((int)HigherRingType).Append(Splitter);
|
||||
sb.Append(RoundCount).Append(Splitter);
|
||||
if (DicTasks != null)
|
||||
{
|
||||
foreach (var kv in DicTasks)
|
||||
sb.Append(kv.Key).Append(Comma);
|
||||
sb.Append(Splitter);
|
||||
foreach (var kv in DicTasks)
|
||||
sb.Append(kv.Value).Append(Comma);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static EventLuckMagicPlayfabData Deserialize(string s)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (s == null || s == "")
|
||||
return null;
|
||||
var tokens = s.Trim(Splitter.ToCharArray()).Split(Splitter);
|
||||
if (tokens.Length != Count)
|
||||
{
|
||||
var e = new Exception($"[EventLuckMagic]Wrong amount of parameters. Expect {Count}, but got {tokens.Length} in \"{s}\".");
|
||||
Debug.LogError(e);
|
||||
return null;
|
||||
}
|
||||
// Debug.Log($"[EventLuckMagic] time string:{tokens[6]}");
|
||||
var keyList = tokens[9].Trim(Comma[0]).Split(Comma).Select(int.Parse).ToList();
|
||||
var valueList = tokens[10].Trim(Comma[0]).Split(Comma).Select(int.Parse).ToList();
|
||||
if (keyList == null || valueList == null || keyList.Count != valueList.Count)
|
||||
{
|
||||
var e = new Exception($"[EventLuckMagic]Dictionary Error.");
|
||||
Debug.LogError(e);
|
||||
return null;
|
||||
}
|
||||
var dicTasks = new Dictionary<int, int>();
|
||||
for (int i = 0; i < keyList.Count; i++)
|
||||
dicTasks.Add(keyList[i], valueList[i]);
|
||||
return new EventLuckMagicPlayfabData
|
||||
{
|
||||
EventId = int.Parse(tokens[0]),
|
||||
TicketCount = int.Parse(tokens[1]),
|
||||
ChainProgress = int.Parse(tokens[2]),
|
||||
InnerState = int.Parse(tokens[3]),
|
||||
MiddleState = int.Parse(tokens[4]),
|
||||
OuterState = int.Parse(tokens[5]),
|
||||
HigherRingExpireTime = ParseTime(tokens[6]),
|
||||
HigherRingType = (EEventLuckMagicRingType)int.Parse(tokens[7]),
|
||||
RoundCount = int.Parse(tokens[8]),
|
||||
DicTasks = dicTasks
|
||||
};
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Log($"[EventLuckMagic] Deserialize error: ");
|
||||
Debug.LogError(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static System.DateTime ParseTime(string timeString)
|
||||
{
|
||||
if (timeString == "")
|
||||
return System.DateTime.MinValue;
|
||||
else
|
||||
try
|
||||
{
|
||||
return System.DateTime.Parse(timeString);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Debug.LogError($"[EventLuckMagic] Failed to parse time string \"{timeString}\"");
|
||||
return System.DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
// Debug.Log($"[EventLuckMagic] Save playfab data : \n" + s);
|
||||
saveStore.Write(Key, s).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
public class EventLuckMagicPlayerPreferenceData
|
||||
{
|
||||
public Vector2? SelectionPosition;
|
||||
public bool DoesSkipAnimation;
|
||||
public static string Key => "EventLuckMagic" + GContext.container.Resolve<IUserService>().UserId;
|
||||
private const char Splitter = '|', Comma = ',';
|
||||
|
||||
public static EventLuckMagicPlayerPreferenceData CreateEmpty()
|
||||
{
|
||||
var res = new EventLuckMagicPlayerPreferenceData();
|
||||
res.SelectionPosition = null;
|
||||
res.DoesSkipAnimation = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(DoesSkipAnimation).Append(Splitter);
|
||||
if (SelectionPosition != null)
|
||||
sb.Append(SelectionPosition.Value.x).Append(Comma).Append(SelectionPosition.Value.y).Append(Splitter);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static EventLuckMagicPlayerPreferenceData Deserialize(string s)
|
||||
{
|
||||
var res = new EventLuckMagicPlayerPreferenceData();
|
||||
if (s == null || s == "")
|
||||
return res;
|
||||
try
|
||||
{
|
||||
var tokens = s.Trim(Splitter).Split(Splitter);
|
||||
res.DoesSkipAnimation = bool.Parse(tokens[0]);
|
||||
if (tokens.Length > 1)
|
||||
{
|
||||
var posTokens = tokens[1].Split(Comma);
|
||||
res.SelectionPosition = new Vector2(float.Parse(posTokens[0]), float.Parse(posTokens[1]));
|
||||
}
|
||||
else
|
||||
res.SelectionPosition = null;
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventLuckMagic] Failed to deserialize:");
|
||||
Debug.LogError(e);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialize();
|
||||
PlayerPrefs.SetString(Key, s);
|
||||
}
|
||||
}
|
||||
|
||||
public class EventLuckMagicEntranceData
|
||||
{
|
||||
public int TicketCount;
|
||||
public DateTime ExpiryTime;
|
||||
public DateTime StartTime;
|
||||
public TimeSpan RemainingTime => ExpiryTime - ZZTimeHelper.UtcNow();
|
||||
public bool IsActive => ZZTimeHelper.UtcNow() >= StartTime && ZZTimeHelper.UtcNow() < ExpiryTime;
|
||||
public string Icon;
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
public class WelcomeBackData
|
||||
{
|
||||
public string lastLoginTime;
|
||||
public string openTime;
|
||||
public string rewardTime;
|
||||
public int received;
|
||||
public int eventCount;
|
||||
public int offlineDays;
|
||||
}
|
||||
public class EventWelcomeBackManager
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
string DataKey_WelcomeBack = "WelcomeBack";
|
||||
Tables tables;
|
||||
public TbEventWelcomeBackConfig eventWelcomeBackConfig;
|
||||
public WelcomeBackData welcomeBackData;
|
||||
public EventWelcomeBackManager()
|
||||
{
|
||||
tables = GContext.container.Resolve<Tables>();
|
||||
eventWelcomeBackConfig = tables.TbEventWelcomeBackConfig;
|
||||
}
|
||||
public WelcomeBackData LoadData()
|
||||
{
|
||||
try
|
||||
{
|
||||
string data = saveStore.Read(DataKey_WelcomeBack).ValueOrThrow();
|
||||
if (data != null)
|
||||
{
|
||||
welcomeBackData = Newtonsoft.Json.JsonConvert.DeserializeObject<WelcomeBackData>(data) ?? throw new Newtonsoft.Json.JsonSerializationException("Null welcome-back save.");
|
||||
}
|
||||
else
|
||||
{
|
||||
welcomeBackData = new WelcomeBackData();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"LoadData failed: {e}");
|
||||
throw;
|
||||
}
|
||||
|
||||
return welcomeBackData;
|
||||
}
|
||||
|
||||
public int GetState(int index, EventWelcomeBackTimeReward timeReward)
|
||||
{
|
||||
if (index < welcomeBackData.received)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
// 仅用于本方法内计算,不修改 welcomeBackData,避免 GetState 产生副作用
|
||||
string baseTimeStr = welcomeBackData.received < 7 ? welcomeBackData.openTime : welcomeBackData.rewardTime;
|
||||
if (string.IsNullOrEmpty(baseTimeStr))
|
||||
return 0;
|
||||
System.DateTime curTime = ZZTimeHelper.UtcNow();
|
||||
System.DateTime baseTime = System.DateTime.Parse(baseTimeStr);
|
||||
return baseTime.AddMinutes(timeReward.Time) <= curTime ? 1 : 0;
|
||||
}
|
||||
|
||||
public int CheckRedPoint()
|
||||
{
|
||||
var dataList = tables.TbEventWelcomeBackTimeReward.DataList;
|
||||
int red = 0;
|
||||
int count = dataList.Count;
|
||||
if (welcomeBackData.received < 7)
|
||||
{
|
||||
count = Math.Min(7, dataList.Count);
|
||||
}
|
||||
for (int i = welcomeBackData.received; i < count; i++)
|
||||
{
|
||||
int state = GetState(i, dataList[i]);
|
||||
if (state == 1)
|
||||
{
|
||||
red++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return red;
|
||||
}
|
||||
|
||||
public List<int> ClaimReward()
|
||||
{
|
||||
var dataList = tables.TbEventWelcomeBackTimeReward.DataList;
|
||||
List<int> dropID = new List<int>();
|
||||
int state = 0;
|
||||
int count = dataList.Count;
|
||||
if (welcomeBackData.received < 7)
|
||||
{
|
||||
count = Math.Min(7, dataList.Count);
|
||||
}
|
||||
int curIndex = welcomeBackData.received;
|
||||
for (int i = welcomeBackData.received; i < count; i++)
|
||||
{
|
||||
state = GetState(i, dataList[i]);
|
||||
if (state == 1)
|
||||
{
|
||||
dropID.Add(dataList[i].DropID);
|
||||
welcomeBackData.received = i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dropID.Count > 0)
|
||||
{
|
||||
if (curIndex < 7 && welcomeBackData.received >= 7)
|
||||
{
|
||||
welcomeBackData.rewardTime = ZZTimeHelper.UtcNow().ToString();
|
||||
}
|
||||
GContext.container.Resolve<PlayerItemData>().AddItemByDropListLureInflation(null, dropID, null, false);
|
||||
SaveData();
|
||||
return dropID;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public System.DateTime GetCurrentRoundEndTime()
|
||||
{
|
||||
System.DateTime baseTime;
|
||||
int windowSeconds;
|
||||
if (welcomeBackData.received < 7)
|
||||
{
|
||||
baseTime = System.DateTime.Parse(welcomeBackData.openTime);
|
||||
windowSeconds = eventWelcomeBackConfig.WelcomeBackTimeRewardWindow[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
baseTime = System.DateTime.Parse(welcomeBackData.rewardTime);
|
||||
windowSeconds = eventWelcomeBackConfig.WelcomeBackTimeRewardWindow[1];
|
||||
}
|
||||
return baseTime.AddSeconds(windowSeconds);
|
||||
}
|
||||
|
||||
public EventWelcomeBackTimeReward GetCurRefreshTime()
|
||||
{
|
||||
if (welcomeBackData.received < tables.TbEventWelcomeBackTimeReward.DataList.Count)
|
||||
{
|
||||
return tables.TbEventWelcomeBackTimeReward.DataList[welcomeBackData.received];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool CheckWelcomeBack()
|
||||
{
|
||||
if (GContext.container.Resolve<PlayerData>().lv < eventWelcomeBackConfig.WelcomeBackConditionLevel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
LoadData();
|
||||
|
||||
if (string.IsNullOrEmpty(welcomeBackData.lastLoginTime))
|
||||
{
|
||||
SetLastLoginTime();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.DateTime lastTime = System.DateTime.Parse(welcomeBackData.lastLoginTime);
|
||||
System.DateTime curTime = ZZTimeHelper.UtcNow();
|
||||
|
||||
if (!string.IsNullOrEmpty(welcomeBackData.openTime))
|
||||
{
|
||||
System.DateTime openTime = System.DateTime.Parse(welcomeBackData.openTime);
|
||||
var offlineTime = curTime - openTime;
|
||||
if (offlineTime.TotalSeconds >= eventWelcomeBackConfig.WelcomeBackCD)
|
||||
{
|
||||
welcomeBackData.openTime = string.Empty;// 超过回归活动CD时间,重置数据
|
||||
SaveData();
|
||||
// 不 return,继续往下判断能否开启新一轮(依据 lastLogin 离线时间)
|
||||
}
|
||||
else if (curTime >= GetCurrentRoundEndTime()
|
||||
|| welcomeBackData.received >= tables.TbEventWelcomeBackTimeReward.DataList.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(welcomeBackData.openTime))
|
||||
{
|
||||
var offlineTime = curTime - lastTime;
|
||||
if (offlineTime.TotalSeconds >= eventWelcomeBackConfig.WelcomeBackConditionTime)
|
||||
{
|
||||
welcomeBackData.openTime = curTime.ToString();
|
||||
welcomeBackData.rewardTime = curTime.ToString();
|
||||
welcomeBackData.received = 0;
|
||||
welcomeBackData.eventCount++;
|
||||
welcomeBackData.offlineDays = (int)offlineTime.TotalDays;
|
||||
SetLastLoginTime();
|
||||
GContext.container.Resolve<IFaceUIService>().AddFaceCustomUIData(new UIType("ComebackGiftPopupPanel"));
|
||||
GContext.container.Resolve<IFaceUIService>().AddFaceCustomUIData(new UIType("ComebackChainsPopupPanel"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
SetLastLoginTime();
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetLastLoginTime()
|
||||
{
|
||||
if (welcomeBackData == null)
|
||||
{
|
||||
welcomeBackData = new WelcomeBackData();
|
||||
}
|
||||
welcomeBackData.lastLoginTime = ZZTimeHelper.UtcNow().ToString();
|
||||
SaveData();
|
||||
}
|
||||
|
||||
void SaveData()
|
||||
{
|
||||
saveStore.Write(DataKey_WelcomeBack, Newtonsoft.Json.JsonConvert.SerializeObject(welcomeBackData)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
public struct ClaimComeBack
|
||||
{
|
||||
public int state;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,88 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class InfiniteBuildingData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int Lvl, Step, StepLvl = 1, LastHangingBlock = -1;
|
||||
public List<BlockRecord> Record = new List<BlockRecord>();
|
||||
// private readonly TbConstructionUnits BlocksTable = GContext.container.Resolve<Tables>().TbConstructionUnits;
|
||||
public int ConstructPrice => (int)
|
||||
(GContext.container.Resolve<Tables>().TbMapData.DataList[^1].ConstrutionCost
|
||||
* GContext.container.Resolve<Tables>().TbConstructionInfiniteConfig.CashMagList[Step]);
|
||||
public struct BlockRecord
|
||||
{
|
||||
public readonly int ID;
|
||||
public Vector2 Pos;
|
||||
public readonly float Height => GContext.container.Resolve<Tables>().TbConstructionUnits[ID].Height;
|
||||
public readonly string PrefabID => GContext.container.Resolve<Tables>().TbConstructionUnits[ID].Prefab;
|
||||
public BlockRecord(int id = 0, Vector2 pos = default)
|
||||
{
|
||||
this.ID = id;
|
||||
this.Pos = pos;
|
||||
}
|
||||
public BlockRecord(int id = 0, float x = 0, float y = 0)
|
||||
{
|
||||
this.ID = id;
|
||||
Pos = new Vector2(x, y);
|
||||
}
|
||||
public readonly override string ToString() => $"{ID},{Pos.x},{Pos.y}";
|
||||
}
|
||||
private string Serialize()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.Append($"{Lvl},{Step},{StepLvl},{LastHangingBlock},");
|
||||
foreach (BlockRecord node in Record)
|
||||
sb.Append($"{node.ToString()},");
|
||||
return sb.ToString().Trim(',');
|
||||
}
|
||||
public void Deserialize(string s)
|
||||
{
|
||||
string[] tokens = s.Split(',');
|
||||
if (tokens == null || tokens.Length < 4 || tokens.Length % 3 != 1)
|
||||
{
|
||||
Debug.LogWarning($"string \"{s}\" cannot be deserialized.");
|
||||
return;
|
||||
}
|
||||
Lvl = int.Parse(tokens[0]);
|
||||
Step = int.Parse(tokens[1]);
|
||||
StepLvl = int.Parse(tokens[2]);
|
||||
LastHangingBlock = int.Parse(tokens[3]);
|
||||
Record = new List<BlockRecord>();
|
||||
for (int i = 4; i < tokens.Length; i+=3)
|
||||
{
|
||||
Record.Add(new BlockRecord(int.Parse(tokens[i]),
|
||||
float.Parse(tokens[i + 1]), float.Parse(tokens[i + 2])));
|
||||
//Debug.Log($"{int.Parse(tokens[i])}, {float.Parse(tokens[i + 1])}, {float.Parse(tokens[i + 2])}");
|
||||
}
|
||||
}
|
||||
public void UploadData()
|
||||
{
|
||||
string s = Serialize();
|
||||
//Debug.Log("Upload!!!!!!!!!!!!!!");
|
||||
//Debug.Log(s);
|
||||
GContext.container.Resolve<PlayerData>().InfiniteBuildingData = s;
|
||||
saveStore.Write("InfiniteBuildingData", s).RequireAccepted();
|
||||
}
|
||||
|
||||
public void DownloadData(string s)
|
||||
{
|
||||
Deserialize(s);
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
public void PrintSelf()
|
||||
{
|
||||
Debug.Log($"Lvl: {Lvl}");
|
||||
Debug.Log($"Step: {Step}");
|
||||
Debug.Log($"StepLvl: {StepLvl}");
|
||||
foreach (BlockRecord node in Record)
|
||||
{
|
||||
Debug.Log($"node: {node.PrefabID}, {node.Pos}");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
public class LuckyADTaskPopupPanel : LuckyTaskPanelBase
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
List<LuckyTaskItemData> taskList;
|
||||
List<LuckyADTaskItem> taskItems = new List<LuckyADTaskItem>();
|
||||
List<LuckyADTaskItemData> taskADList = new List<LuckyADTaskItemData>();
|
||||
LuckyADTask luckyTaskItem;
|
||||
TMP_Text text_time;
|
||||
Timer timer;
|
||||
System.DateTime refreshTime;
|
||||
protected override void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
base.Awake();
|
||||
luckyTaskItem = transform.Find("root/hook_missions_object").GetComponent<LuckyADTask>();
|
||||
text_time = transform.Find("root/text_time").GetComponent<TMP_Text>();
|
||||
}
|
||||
protected override void SetItem()
|
||||
{
|
||||
taskList = new List<LuckyTaskItemData>();
|
||||
int level = taskData.GetValueOrDefault(-2, 0);
|
||||
float inflationRate = taskData.GetValueOrDefault(-3, 0) / 100f;
|
||||
foreach (var item in taskData)
|
||||
{
|
||||
if (item.Key <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
CommonTask commonTask = tables.TbCommonTask.Get(item.Key);
|
||||
if (commonTask != null)
|
||||
{
|
||||
LuckyTaskItemData luckyTaskData = new LuckyTaskItemData();
|
||||
luckyTaskData.id = item.Key;
|
||||
int dropID = commonTask.TaskReward[level];
|
||||
|
||||
luckyTaskData.reward = dropID;
|
||||
|
||||
luckyTaskData.inflationRate = inflationRate;
|
||||
string desc = LocalizationMgr.GetText(commonTask.TaskDesc_l10n_key);
|
||||
int taskParam = commonTask.TaskParam[level];
|
||||
if (commonTask.TaskType == ConditionType.ConsumeEnergy)
|
||||
{
|
||||
taskParam = GContext.container.Resolve<PlayerItemData>().IntInflation(taskParam, inflationRate);
|
||||
}
|
||||
luckyTaskData.target = taskParam;
|
||||
luckyTaskData.progress = taskParam;
|
||||
if (item.Value == -1)
|
||||
{
|
||||
luckyTaskData.state = -1;
|
||||
}
|
||||
else if (item.Value >= taskParam)
|
||||
{
|
||||
luckyTaskData.state = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
luckyTaskData.progress = item.Value;
|
||||
luckyTaskData.state = 0;
|
||||
}
|
||||
desc = desc.SafeFormat(taskParam);
|
||||
luckyTaskData.desc = desc;
|
||||
taskList.Add(luckyTaskData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Task ID {item.Key} not found in CommonTask table.");
|
||||
}
|
||||
}
|
||||
taskList.Sort((a, b) => b.state.CompareTo(a.state)); // 按state排序
|
||||
luckyTaskItem.Init(taskList[0], OnClickLuckyTaskItem);
|
||||
SetADItem();
|
||||
}
|
||||
void SetADItem()
|
||||
{
|
||||
taskADList = SetADItemData();
|
||||
for (int i = 0; i < taskADList.Count; i++)
|
||||
{
|
||||
LuckyADTaskItem luckyTaskItem = Instantiate(itemPrefab, content).AddComponent<LuckyADTaskItem>();
|
||||
luckyTaskItem.gameObject.SetActive(true);
|
||||
luckyTaskItem.Init(taskADList[i], OnBuySuccess);
|
||||
taskItems.Add(luckyTaskItem);
|
||||
}
|
||||
//设置倒计时
|
||||
var nowTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
refreshTime = nowTime.Date.AddDays(1);
|
||||
TimeSpan now = refreshTime - ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
text_time.text = LocalizationMgr.GetFormatTextValue("UI_COMMON_refresh",
|
||||
ConvertTools.ConvertTime2(now.Days, now.Hours, now.Minutes, now.Seconds));
|
||||
double seconds = now.TotalSeconds;
|
||||
if (seconds < 1)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
timer = this.AttachTimer((float)seconds, Refresh,
|
||||
(elapsed) =>
|
||||
{
|
||||
now = refreshTime - ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
text_time.text = LocalizationMgr.GetFormatTextValue("UI_COMMON_refresh",
|
||||
ConvertTools.ConvertTime2(now.Days, now.Hours, now.Minutes, now.Seconds));
|
||||
}, useRealTime: true);
|
||||
}
|
||||
}
|
||||
void Refresh()
|
||||
{
|
||||
timer?.Cancel();
|
||||
timer = null;
|
||||
UIManager.Instance.DestroyUI(gameObject.name);
|
||||
}
|
||||
List<LuckyADTaskItemData> SetADItemData()
|
||||
{
|
||||
int round = taskData.GetValueOrDefault(-1, 0);
|
||||
float inflationRate = taskData.GetValueOrDefault(-3, 0) / 100f;
|
||||
int group = taskData.GetValueOrDefault(-4, 0);
|
||||
string adData = saveStore.Read(LuckyTaskData.ADTaskADKey).ValueOrThrow();
|
||||
int index = 0;
|
||||
int curDay = ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear;
|
||||
if (!string.IsNullOrEmpty(adData))
|
||||
{
|
||||
string[] strings = adData.Split("|");
|
||||
int day = -1;
|
||||
if (strings.Length == 2 && int.TryParse(strings[1], out day) && day == curDay)
|
||||
{
|
||||
int.TryParse(strings[0], out index);
|
||||
}
|
||||
else
|
||||
{
|
||||
adData = $"{index}|{curDay}";
|
||||
saveStore.Write(LuckyTaskData.ADTaskADKey, adData).RequireAccepted();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
adData = $"{index}|{curDay}";
|
||||
saveStore.Write(LuckyTaskData.ADTaskADKey, adData).RequireAccepted();
|
||||
}
|
||||
List<LuckyADTaskItemData> taskADList = new List<LuckyADTaskItemData>();
|
||||
TbEventLuckyTask eventLuckyTask = LuckyTaskData.GetTbEventLuckyTask(luckyModel.EventId, round, group);
|
||||
var adReward = eventLuckyTask.adReward;
|
||||
for (int i = 0; i < adReward.Count; i++)
|
||||
{
|
||||
int dropID = adReward[i];
|
||||
LuckyADTaskItemData luckyTaskData = new LuckyADTaskItemData();
|
||||
luckyTaskData.reward = dropID;
|
||||
luckyTaskData.inflationRate = inflationRate;
|
||||
luckyTaskData.index = i;
|
||||
if (i < index)
|
||||
{
|
||||
luckyTaskData.state = -1;
|
||||
}
|
||||
else if (i == index)
|
||||
{
|
||||
luckyTaskData.state = 1;
|
||||
}
|
||||
taskADList.Add(luckyTaskData);
|
||||
}
|
||||
taskADList.Sort((a, b) => b.state.CompareTo(a.state)); // 按state排序
|
||||
return taskADList;
|
||||
}
|
||||
void OnBuySuccess(LuckyADTaskItem luckyADTaskItem)
|
||||
{
|
||||
canvasGroup.blocksRaycasts = false;
|
||||
//领奖
|
||||
int dropID = luckyADTaskItem.data.reward;
|
||||
|
||||
ChangeSource changeSource = new ChangeSource(EventId, "Event_Minigame3", "Event_Minigame3_LuckMagic", "TaskReward");
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
changeSource.Source_Type = "Event_Minigame3_LuckyCards";
|
||||
}
|
||||
List<ItemData> rewardItem = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(luckyTaskItem.data.inflationRate, dropID, changeSource, false);
|
||||
|
||||
luckyADTaskItem.Play();
|
||||
|
||||
string adData = saveStore.Read(LuckyTaskData.ADTaskADKey).ValueOrThrow();
|
||||
int index = 0;
|
||||
int day = ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear;
|
||||
if (!string.IsNullOrEmpty(adData))
|
||||
{
|
||||
string[] strings = adData.Split("|");
|
||||
if (strings.Length == 2)
|
||||
{
|
||||
int.TryParse(strings[0], out index);
|
||||
int.TryParse(strings[1], out day);
|
||||
}
|
||||
}
|
||||
//保存进度
|
||||
index++;
|
||||
adData = $"{index}|{day}";
|
||||
saveStore.Write(LuckyTaskData.ADTaskADKey, adData).RequireAccepted();
|
||||
RefreshADItemState(index);
|
||||
canvasGroup.blocksRaycasts = true;
|
||||
GContext.Publish(new ShowData());
|
||||
}
|
||||
void RefreshADItemState(int index)
|
||||
{
|
||||
for (int i = 0; i < taskADList.Count; i++)
|
||||
{
|
||||
taskADList[i].state = 0;
|
||||
if (taskADList[i].index < index)
|
||||
{
|
||||
taskADList[i].state = -1;
|
||||
}
|
||||
else if (taskADList[i].index == index)
|
||||
{
|
||||
taskADList[i].state = 1;
|
||||
}
|
||||
taskItems[i].Refresh();
|
||||
}
|
||||
}
|
||||
void RefreshAdItem()
|
||||
{
|
||||
|
||||
}
|
||||
public void TaskItemPlay(LuckyADTask luckyTaskItem)
|
||||
{
|
||||
canvasGroup.blocksRaycasts = false;
|
||||
try
|
||||
{
|
||||
luckyTaskItem.data.state = -1; // 更新状态为已领取
|
||||
luckyTaskItem.Refresh();
|
||||
luckyTaskItem.Play();
|
||||
taskList.Sort((a, b) => b.state.CompareTo(a.state)); // 按state排序
|
||||
luckyTaskItem.PlayFillAmount(taskList[0]);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error during TaskItemPlay: {ex.Message}");
|
||||
}
|
||||
canvasGroup.blocksRaycasts = true;
|
||||
GContext.Publish(new ShowData());
|
||||
}
|
||||
|
||||
void OnClickLuckyTaskItem(LuckyADTask luckyTaskItem)
|
||||
{
|
||||
if (IsOpen)
|
||||
{
|
||||
//奖励补领
|
||||
OnClickLuckyTaskItem1(luckyTaskItem);
|
||||
return;
|
||||
}
|
||||
if (luckyTaskItem.data.state == 1)
|
||||
{
|
||||
bool result = luckyModel.OnGetTaskReward(luckyTaskItem.data.id);
|
||||
if (result)
|
||||
{
|
||||
CommonTask commonTask = tables.TbCommonTask.GetOrDefault(luckyTaskItem.data.id);
|
||||
if (commonTask != null)
|
||||
{
|
||||
// 任务奖励
|
||||
int level = taskData.GetValueOrDefault(-2, 0);
|
||||
int dropID = commonTask.TaskReward[level];
|
||||
|
||||
ChangeSource changeSource = new ChangeSource(EventId, "Event_Minigame3", "Event_Minigame3_LuckMagic", "TaskReward");
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
changeSource.Source_Type = "Event_Minigame3_LuckyCards";
|
||||
}
|
||||
List<ItemData> rewardItem = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(luckyTaskItem.data.inflationRate, dropID, changeSource, false);
|
||||
if (rewardItem != null)
|
||||
{
|
||||
TaskItemPlay(luckyTaskItem);
|
||||
}
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("event_lucky_task"))
|
||||
{
|
||||
int round = luckyModel.DicTasks.GetValueOrDefault(-1, 0);
|
||||
e.AddContent("task_id", luckyTaskItem.data.id)
|
||||
.AddContent("round", round)
|
||||
.AddContent("combine_id", round * 100000 + luckyTaskItem.data.id % 10000)
|
||||
.AddContent("task_reward", dropID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("任务奖励领取失败");
|
||||
}
|
||||
}
|
||||
else if (luckyTaskItem.data.state == 0)
|
||||
{
|
||||
OnClickGo(luckyTaskItem.data.id);
|
||||
Debug.Log("任务未完成,无法领取奖励");
|
||||
}
|
||||
else if (luckyTaskItem.data.state == -1)
|
||||
{
|
||||
Debug.Log("任务已领取,无法重复领取");
|
||||
}
|
||||
}
|
||||
|
||||
void OnClickLuckyTaskItem1(LuckyADTask luckyTaskItem)
|
||||
{
|
||||
if (luckyTaskItem.data.state == 1)
|
||||
{
|
||||
CommonTask commonTask = tables.TbCommonTask.GetOrDefault(luckyTaskItem.data.id);
|
||||
taskData[luckyTaskItem.data.id] = -1; // 标记为已领取
|
||||
if (commonTask != null)
|
||||
{
|
||||
// 任务奖励
|
||||
int level = taskData.GetValueOrDefault(-2, 0);
|
||||
int dropID = commonTask.TaskReward[level];
|
||||
ChangeSource changeSource = new ChangeSource(EventId, "Event_Minigame3", "Event_Minigame3_LuckMagic", "TaskReward");
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
changeSource.Source_Type = "Event_Minigame3_LuckyCards";
|
||||
}
|
||||
|
||||
List<ItemData> rewardItem = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(luckyTaskItem.data.inflationRate, dropID, changeSource, false);
|
||||
if (rewardItem != null)
|
||||
{
|
||||
TaskItemPlay(luckyTaskItem);
|
||||
}
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("event_lucky_task"))
|
||||
{
|
||||
int round = luckyModel.DicTasks.GetValueOrDefault(-1, 0);
|
||||
e.AddContent("task_id", luckyTaskItem.data.id)
|
||||
.AddContent("round", round)
|
||||
.AddContent("combine_id", round * 100000 + luckyTaskItem.data.id % 10000)
|
||||
.AddContent("task_reward", dropID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
UIManager.Instance.DestroyUI(gameObject.name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class LuckyTaskBtn : MonoBehaviour, IUIRedPoint
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
Button btn;
|
||||
ILuckyTaskGetData model;
|
||||
|
||||
public string key;
|
||||
public GameObject redPoint;
|
||||
public GameObject redpoint_big;
|
||||
public TMP_Text text_redpoint_big;
|
||||
void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
btn = GetComponent<Button>();
|
||||
btn.onClick.AddListener(OnClick);
|
||||
}
|
||||
public void Init(ILuckyTaskGetData model, string key)
|
||||
{
|
||||
this.model = model;
|
||||
this.key = key;
|
||||
SetKey();
|
||||
}
|
||||
async void OnClick()
|
||||
{
|
||||
if (model != null)
|
||||
{
|
||||
GameObject taskPanelName = await UIManager.Instance.ShowUI(new UIType(model.TaskPanelName));
|
||||
if (this == null || !saveStore.IsCurrent) return;
|
||||
if (taskPanelName != null)
|
||||
{
|
||||
var panel = taskPanelName.GetComponent<LuckyTaskPanelBase>();
|
||||
if (panel != null)
|
||||
{
|
||||
panel.Init(model);
|
||||
int count = RedPointManager.Instance.GetRedPointCount(key);
|
||||
RedPointManager.Instance.SetRedPointState(key, count > 0, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(key))
|
||||
{
|
||||
RedPointManager.Instance.RemoveRedPoint(key);
|
||||
key = "";
|
||||
}
|
||||
}
|
||||
public void SetKey()
|
||||
{
|
||||
RedPointManager.Instance.AddRedPoint(key, this);
|
||||
SetRedPointState(RedPointManager.Instance.GetRedPointState(key));
|
||||
}
|
||||
|
||||
public void SetRedPointState(bool state)
|
||||
{
|
||||
int count = RedPointManager.Instance.GetRedPointCount(key);
|
||||
if (!state)
|
||||
{
|
||||
string adData = saveStore.Read(LuckyTaskData.ADTaskADKey).ValueOrThrow();
|
||||
if (!string.IsNullOrEmpty(adData))
|
||||
{
|
||||
string[] strings = adData.Split("|");
|
||||
int day = -1;
|
||||
int curDay = ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear;
|
||||
if (strings.Length != 2 || !int.TryParse(strings[1], out day) || day != curDay)
|
||||
{
|
||||
state = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = true;
|
||||
}
|
||||
}
|
||||
if (redpoint_big != null && text_redpoint_big != null)
|
||||
{
|
||||
if (count > 1)
|
||||
{
|
||||
redpoint_big.SetActive(true);
|
||||
if (count > 99)
|
||||
{
|
||||
text_redpoint_big.text = "99+";
|
||||
}
|
||||
else
|
||||
{
|
||||
text_redpoint_big.text = count.ToString();
|
||||
}
|
||||
state = false;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
redpoint_big.SetActive(false);
|
||||
}
|
||||
}
|
||||
if (redPoint != null)
|
||||
{
|
||||
redPoint.SetActive(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class LuckyTaskPanel : LuckyTaskPanelBase
|
||||
{
|
||||
protected override void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
base.Awake();
|
||||
}
|
||||
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
/// <summary>
|
||||
/// luckyModel 也作为是否自动全部领奖的标志
|
||||
/// </summary>
|
||||
List<LuckyTaskItem> taskItems = new List<LuckyTaskItem>();
|
||||
protected override void SetItem()
|
||||
{
|
||||
string adData = saveStore.Read(LuckyTaskData.ADTaskADKey).ValueOrThrow();
|
||||
int curDay = ZZTimeHelper.UtcNow().UtcNowOffset().DayOfYear;
|
||||
if (!string.IsNullOrEmpty(adData))
|
||||
{
|
||||
string[] strings = adData.Split("|");
|
||||
int day = -1;
|
||||
if (strings.Length != 2 || !int.TryParse(strings[1], out day) || day != curDay)
|
||||
{
|
||||
adData = $"{0}|{curDay}";
|
||||
saveStore.Write(LuckyTaskData.ADTaskADKey, adData).RequireAccepted();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
adData = $"{0}|{curDay}";
|
||||
saveStore.Write(LuckyTaskData.ADTaskADKey, adData).RequireAccepted();
|
||||
}
|
||||
List<LuckyTaskItemData> taskList = new List<LuckyTaskItemData>();
|
||||
int level = taskData.GetValueOrDefault(-2, 0);
|
||||
float inflationRate = taskData.GetValueOrDefault(-3, 0) / 100f;
|
||||
foreach (var item in taskData)
|
||||
{
|
||||
if (item.Key <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
CommonTask commonTask = tables.TbCommonTask.Get(item.Key);
|
||||
if (commonTask != null)
|
||||
{
|
||||
LuckyTaskItemData luckyTaskData = new LuckyTaskItemData();
|
||||
luckyTaskData.id = item.Key;
|
||||
int dropID = commonTask.TaskReward[level];
|
||||
|
||||
luckyTaskData.reward = dropID;
|
||||
|
||||
luckyTaskData.inflationRate = inflationRate;
|
||||
string desc = LocalizationMgr.GetText(commonTask.TaskDesc_l10n_key);
|
||||
string progressText;
|
||||
if (commonTask.TaskType == ConditionType.RechargeAmount)
|
||||
{
|
||||
//sku.ProdPrice
|
||||
int iapid = commonTask.TaskParam[level];
|
||||
var iap = tables.TbIAPItemList.GetOrDefault(iapid);
|
||||
SKUDetailDataEvent sKUDetailDataEvent = new SKUDetailDataEvent(iap);
|
||||
GContext.Publish(sKUDetailDataEvent);
|
||||
desc = desc.SafeFormat(sKUDetailDataEvent.price);
|
||||
float TaskParam = iap.VIPPoint;
|
||||
float value = item.Value;
|
||||
float ratio = value / TaskParam;
|
||||
float ProdPrice = sKUDetailDataEvent.prodPrice;
|
||||
float truncatedPrice = (float)Mathf.Ceil(ratio * ProdPrice * 100) / 100;
|
||||
progressText = $"({truncatedPrice:F2}/{ProdPrice})";
|
||||
if (item.Value == -1)
|
||||
{
|
||||
progressText = $"({ProdPrice}/{ProdPrice})";
|
||||
luckyTaskData.state = -1;
|
||||
}
|
||||
else if (value >= TaskParam)
|
||||
{
|
||||
luckyTaskData.state = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
luckyTaskData.state = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int taskParam = commonTask.TaskParam[level];
|
||||
if (commonTask.TaskType == ConditionType.ConsumeEnergy)
|
||||
{
|
||||
taskParam = GContext.container.Resolve<PlayerItemData>().IntInflation(taskParam, inflationRate);
|
||||
}
|
||||
progressText = $"({item.Value}/{taskParam})";
|
||||
if (item.Value == -1)
|
||||
{
|
||||
progressText = $"({taskParam}/{taskParam})";
|
||||
luckyTaskData.state = -1;
|
||||
}
|
||||
else if (item.Value >= taskParam)
|
||||
{
|
||||
luckyTaskData.state = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
luckyTaskData.state = 0;
|
||||
}
|
||||
desc = desc.SafeFormat(taskParam);
|
||||
}
|
||||
luckyTaskData.desc = desc + progressText;
|
||||
taskList.Add(luckyTaskData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Task ID {item.Key} not found in CommonTask table.");
|
||||
}
|
||||
}
|
||||
taskList.Sort((a, b) => b.state.CompareTo(a.state)); // 按state排序
|
||||
for (int i = 0; i < taskList.Count; i++)
|
||||
{
|
||||
LuckyTaskItem luckyTaskItem = Instantiate(itemPrefab, content).AddComponent<LuckyTaskItem>();
|
||||
luckyTaskItem.gameObject.SetActive(true);
|
||||
luckyTaskItem.Init(taskList[i], OnClickLuckyTaskItem);
|
||||
taskItems.Add(luckyTaskItem);
|
||||
}
|
||||
}
|
||||
|
||||
public void TaskItemPlay(LuckyTaskItem luckyTaskItem)
|
||||
{
|
||||
canvasGroup.blocksRaycasts = false;
|
||||
try
|
||||
{
|
||||
luckyTaskItem.data.state = -1; // 更新状态为已领取
|
||||
luckyTaskItem.Refresh();
|
||||
luckyTaskItem.Play();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error during TaskItemPlay: {ex.Message}");
|
||||
}
|
||||
canvasGroup.blocksRaycasts = true;
|
||||
GContext.Publish(new ShowData());
|
||||
}
|
||||
|
||||
void OnClickLuckyTaskItem(LuckyTaskItem luckyTaskItem)
|
||||
{
|
||||
if (IsOpen)
|
||||
{
|
||||
//奖励补领
|
||||
OnClickLuckyTaskItem1(luckyTaskItem);
|
||||
return;
|
||||
}
|
||||
if (luckyTaskItem.data.state == 1)
|
||||
{
|
||||
bool result = luckyModel.OnGetTaskReward(luckyTaskItem.data.id);
|
||||
if (result)
|
||||
{
|
||||
CommonTask commonTask = tables.TbCommonTask.GetOrDefault(luckyTaskItem.data.id);
|
||||
if (commonTask != null)
|
||||
{
|
||||
// 任务奖励
|
||||
int level = taskData.GetValueOrDefault(-2, 0);
|
||||
int dropID = commonTask.TaskReward[level];
|
||||
|
||||
ChangeSource changeSource = new ChangeSource(EventId, "Event_Minigame3", "Event_Minigame3_LuckMagic", "TaskReward");
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
changeSource.Source_Type = "Event_Minigame3_LuckyCards";
|
||||
}
|
||||
List<ItemData> rewardItem = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(luckyTaskItem.data.inflationRate, dropID, changeSource, false);
|
||||
if (rewardItem != null)
|
||||
{
|
||||
TaskItemPlay(luckyTaskItem);
|
||||
}
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("event_lucky_task"))
|
||||
{
|
||||
int round = luckyModel.DicTasks.GetValueOrDefault(-1, 0);
|
||||
e.AddContent("task_id", luckyTaskItem.data.id)
|
||||
.AddContent("round", round)
|
||||
.AddContent("combine_id", round * 100000 + luckyTaskItem.data.id % 10000)
|
||||
.AddContent("task_reward", dropID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("任务奖励领取失败");
|
||||
}
|
||||
}
|
||||
else if (luckyTaskItem.data.state == 0)
|
||||
{
|
||||
OnClickGo(luckyTaskItem.data.id);
|
||||
Debug.Log("任务未完成,无法领取奖励");
|
||||
}
|
||||
else if (luckyTaskItem.data.state == -1)
|
||||
{
|
||||
Debug.Log("任务已领取,无法重复领取");
|
||||
}
|
||||
}
|
||||
|
||||
void OnClickLuckyTaskItem1(LuckyTaskItem luckyTaskItem)
|
||||
{
|
||||
if (luckyTaskItem.data.state == 1)
|
||||
{
|
||||
CommonTask commonTask = tables.TbCommonTask.GetOrDefault(luckyTaskItem.data.id);
|
||||
taskData[luckyTaskItem.data.id] = -1; // 标记为已领取
|
||||
if (commonTask != null)
|
||||
{
|
||||
// 任务奖励
|
||||
int level = taskData.GetValueOrDefault(-2, 0);
|
||||
int dropID = commonTask.TaskReward[level];
|
||||
ChangeSource changeSource = new ChangeSource(EventId, "Event_Minigame3", "Event_Minigame3_LuckMagic", "TaskReward");
|
||||
if (fishingEvent.SubType == 4)
|
||||
{
|
||||
changeSource.Source_Type = "Event_Minigame3_LuckyCards";
|
||||
}
|
||||
|
||||
List<ItemData> rewardItem = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(luckyTaskItem.data.inflationRate, dropID, changeSource, false);
|
||||
if (rewardItem != null)
|
||||
{
|
||||
TaskItemPlay(luckyTaskItem);
|
||||
}
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("event_lucky_task"))
|
||||
{
|
||||
int round = luckyModel.DicTasks.GetValueOrDefault(-1, 0);
|
||||
e.AddContent("task_id", luckyTaskItem.data.id)
|
||||
.AddContent("round", round)
|
||||
.AddContent("combine_id", round * 100000 + luckyTaskItem.data.id % 10000)
|
||||
.AddContent("task_reward", dropID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
UIManager.Instance.DestroyUI(gameObject.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
public class LuckyTaskItemData
|
||||
{
|
||||
public int id;
|
||||
public string desc;
|
||||
public int target;
|
||||
public int reward;
|
||||
public int progress;
|
||||
/// <summary>
|
||||
/// // 0:未完成 1:可领取 -1:已领取
|
||||
/// </summary>
|
||||
public int state;
|
||||
public float inflationRate;
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
public class MatchGameData
|
||||
{
|
||||
public int eventID;
|
||||
public int index;
|
||||
public int vip;
|
||||
public float inflation;
|
||||
public int seed;
|
||||
public List<int> isOpenedList = new List<int>();
|
||||
//连续翻开不同的次数
|
||||
public int openCount;
|
||||
//兔子交换记录,-1 表示无交换
|
||||
public int swapFrom = -1;
|
||||
public int swapTo = -1;
|
||||
public int swapFrom2 = -1;
|
||||
public int swapTo2 = -1;
|
||||
public int tokenConsumed;
|
||||
}
|
||||
|
||||
public enum FlipResult
|
||||
{
|
||||
// InsufficientTokens,
|
||||
FirstHatFlipped,
|
||||
NoMatch,
|
||||
Match,
|
||||
}
|
||||
|
||||
|
||||
public class EventMatchGameManager
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public string EventMatchGameDataKey => "EventMatchGameDataKey";
|
||||
public int ItemSubType => 3;
|
||||
public int ItemType => 6;
|
||||
int EventType => 4;
|
||||
int EventSubType => 13;
|
||||
Tables tables;
|
||||
FishingEventData fishingEventData;
|
||||
TbFishingEvent tbFishingEvent;
|
||||
TbFishingEventCycleItem tbCycleItem;
|
||||
TbEventMatchInit tbEventMatchInit;
|
||||
public EventMatchGameManager()
|
||||
{
|
||||
tables = GContext.container.Resolve<Tables>();
|
||||
fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
tbFishingEvent = tables.TbFishingEvent;
|
||||
tbCycleItem = tables.TbFishingEventCycleItem;
|
||||
tbEventMatchInit = tables.TbEventMatchInit;
|
||||
}
|
||||
|
||||
public MatchGameData GetEventData()
|
||||
{
|
||||
var matchGameData = new MatchGameData();
|
||||
|
||||
string data = saveStore.Read(EventMatchGameDataKey).ValueOrThrow();
|
||||
if (data != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
matchGameData = Newtonsoft.Json.JsonConvert.DeserializeObject<MatchGameData>(data) ?? throw new Newtonsoft.Json.JsonSerializationException("Null MatchGameData save.");
|
||||
if (matchGameData.vip > 10)
|
||||
{
|
||||
matchGameData.vip = 10;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return matchGameData;
|
||||
}
|
||||
|
||||
public MatchGameData CheckEventData()
|
||||
{
|
||||
MatchGameData matchGameData = GetEventData();
|
||||
FishingEvent tr = GetEvent();
|
||||
if (tr == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (matchGameData.eventID != tr.ID)
|
||||
{
|
||||
FishingEventCycleItem cycleItem = tbCycleItem.GetOrDefault(tr.RedirectID);
|
||||
matchGameData = new MatchGameData();
|
||||
matchGameData.eventID = tr.ID;
|
||||
matchGameData.inflation = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
matchGameData.seed = new System.Random().Next();
|
||||
matchGameData.index = 0;
|
||||
matchGameData.vip = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
if (matchGameData.vip > 10)
|
||||
{
|
||||
matchGameData.vip = 10;
|
||||
}
|
||||
SetTokens(tr.ID, cycleItem.WelcomeGift);
|
||||
SaveData(matchGameData);
|
||||
}
|
||||
return matchGameData;
|
||||
}
|
||||
|
||||
public bool CheckOpen()
|
||||
{
|
||||
var matchGameData = CheckEventData();
|
||||
if (matchGameData == null || matchGameData.eventID == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FishingEvent fishingEvent = tables.TbFishingEvent.GetOrDefault(matchGameData.eventID);
|
||||
if (fishingEvent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LimitedTime limitedTime = fishingEvent.TimeDefinition as LimitedTime;
|
||||
var startTime = DateTime.Parse(limitedTime.StartTime);
|
||||
var endTime = DateTime.Parse(limitedTime.EndTime);
|
||||
var curTime = ZZTimeHelper.UtcNow();
|
||||
if (curTime < startTime || curTime > endTime)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FishingEvent GetEvent()
|
||||
{
|
||||
return fishingEventData.GetEventByTypeAndSubType(EventType, EventSubType);
|
||||
}
|
||||
|
||||
#region Tokens
|
||||
public void AddTokens(int subType, int count)
|
||||
{
|
||||
if (subType == ItemSubType)
|
||||
{
|
||||
FishingEvent tr = GetEvent();
|
||||
if (tr != null)
|
||||
{
|
||||
int tokens = GetTokenCount(tr.ID);
|
||||
tokens += count;
|
||||
SetTokens(tr.ID, tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
void SetTokens(int eventID, int count)
|
||||
{
|
||||
fishingEventData.SaveTransitionData(eventID, count);
|
||||
}
|
||||
|
||||
public void RemoveTokens(int eventID, int count)
|
||||
{
|
||||
int tokens = GetTokenCount(eventID);
|
||||
tokens -= count;
|
||||
SetTokens(eventID, tokens);
|
||||
}
|
||||
|
||||
public int GetTokenCount()
|
||||
{
|
||||
FishingEvent tr = GetEvent();
|
||||
if (tr != null)
|
||||
{
|
||||
return GetTokenCount(tr.ID);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public int GetTokenCount(int eventID)
|
||||
{
|
||||
return fishingEventData.GetTransitionDataCount(eventID);
|
||||
}
|
||||
#endregion Tokens
|
||||
|
||||
public FlipResult AddProgress(int eventID, MatchGameData data, List<int> eventSpecialItemList, int opened, List<int> board, out List<int> openPos)
|
||||
{
|
||||
openPos = new List<int>();
|
||||
FlipResult idOpened = FlipResult.FirstHatFlipped;
|
||||
RemoveTokens(eventID, 1);
|
||||
data.tokenConsumed++;
|
||||
int count = data.isOpenedList.Count;
|
||||
if (count % 2 == 0)
|
||||
{
|
||||
data.isOpenedList.Add(opened);
|
||||
openPos.Add(opened);
|
||||
}
|
||||
else
|
||||
{
|
||||
//做对比
|
||||
int curOpened = data.isOpenedList[count - 1];
|
||||
//获取 curOpened 的数据,对比 opened 的数据
|
||||
int curId = board[curOpened];
|
||||
int nextId = board[opened];
|
||||
openPos.Add(curOpened);
|
||||
openPos.Add(opened);
|
||||
|
||||
#if AGG
|
||||
EventMatchInit matchInit = GetMatchInit(eventID);
|
||||
if (matchInit != null)
|
||||
{
|
||||
EventMatchStage matchStage = GetMatchStage(data, matchInit);
|
||||
int countR1 = matchInit.StageListRound1.Count;
|
||||
int roundId = data.index < countR1 ? 1 : (data.index - countR1) / matchInit.StageListRound2.Count + 2;
|
||||
using (var e = GEvent.GameEvent("event_match"))
|
||||
{
|
||||
e.AddContent("event_id", eventID)
|
||||
.AddContent("round_id", roundId)
|
||||
.AddContent("stage_id", matchStage.ID)
|
||||
.AddContent("item_id1", curId)
|
||||
.AddContent("item_id2", nextId);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (curId == nextId)
|
||||
{
|
||||
//成功
|
||||
data.isOpenedList.Add(opened);
|
||||
data.openCount = 0;
|
||||
idOpened = FlipResult.Match;
|
||||
if (eventSpecialItemList.Contains(curId))
|
||||
{
|
||||
var spOpenPos = GetSpItemOpen(data, board);
|
||||
if (spOpenPos.Count > 0)
|
||||
{
|
||||
data.isOpenedList.AddRange(spOpenPos);
|
||||
openPos.AddRange(spOpenPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//失败
|
||||
data.isOpenedList.RemoveAt(count - 1);
|
||||
data.openCount++;
|
||||
idOpened = FlipResult.NoMatch;
|
||||
if (data.openCount >= 4)
|
||||
{
|
||||
var tipsPos = GetTipsItemOpen(data, board);
|
||||
if (tipsPos.Count > 0)
|
||||
{
|
||||
openPos.AddRange(tipsPos);
|
||||
}
|
||||
data.openCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
GameDebug.Log("[EventMatch] ItemID " + string.Join(',', board) + " " + string.Join(',', data.isOpenedList));
|
||||
SaveData(data);
|
||||
return idOpened;
|
||||
}
|
||||
List<int> GetSpItemOpen(MatchGameData data, List<int> board)
|
||||
{
|
||||
List<int> openPos = new List<int>();
|
||||
Dictionary<int, int> boardDic = new Dictionary<int, int>();
|
||||
|
||||
for (int i = 0; i < board.Count; i++)
|
||||
{
|
||||
if (!data.isOpenedList.Contains(i))
|
||||
{
|
||||
boardDic.Add(i, board[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (boardDic.Count <= 4)
|
||||
{
|
||||
openPos.AddRange(boardDic.Keys);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Random random = new System.Random();
|
||||
// 执行两次随机选择
|
||||
SelectAndRemoveGroup(boardDic, openPos, random);
|
||||
SelectAndRemoveGroup(boardDic, openPos, random);
|
||||
}
|
||||
return openPos;
|
||||
}
|
||||
|
||||
List<int> GetTipsItemOpen(MatchGameData data, List<int> board)
|
||||
{
|
||||
List<int> openPos = new List<int>();
|
||||
Dictionary<int, int> dict = new Dictionary<int, int>();
|
||||
for (int i = 0; i < board.Count; i++)
|
||||
{
|
||||
if (!data.isOpenedList.Contains(i))
|
||||
{
|
||||
dict.Add(i, board[i]);
|
||||
}
|
||||
}
|
||||
System.Random random = new System.Random();
|
||||
var keys = dict.Keys.ToList();
|
||||
int key = keys[random.Next(keys.Count)];
|
||||
int value = dict[key];
|
||||
// 找到所有具有相同值的键
|
||||
return dict.Where(kv => kv.Value == value).Select(kv => kv.Key).ToList();
|
||||
}
|
||||
|
||||
void SelectAndRemoveGroup(Dictionary<int, int> dict, List<int> result, System.Random random)
|
||||
{
|
||||
var keys = dict.Keys.ToList();
|
||||
int key = keys[random.Next(keys.Count)];
|
||||
int value = dict[key];
|
||||
|
||||
// 找到所有具有相同值的键
|
||||
var group = dict.Where(kv => kv.Value == value).Select(kv => kv.Key).ToList();
|
||||
|
||||
// 添加到结果列表
|
||||
result.AddRange(group);
|
||||
|
||||
// 从字典中移除已选中的组
|
||||
foreach (int k in group)
|
||||
{
|
||||
dict.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public int NextStage()
|
||||
{
|
||||
MatchGameData data = GetEventData();
|
||||
EventMatchInit eventMatchInit = GetMatchInit(data.eventID);
|
||||
|
||||
EventMatchStage stage = GetMatchStage(data, eventMatchInit);
|
||||
if (data.isOpenedList.Count == stage.ItemCount)
|
||||
{
|
||||
#if AGG
|
||||
int countR1 = eventMatchInit.StageListRound1.Count;
|
||||
int roundId = data.index < countR1 ? 1 : (data.index - countR1) / eventMatchInit.StageListRound2.Count + 2;
|
||||
using (var e = GEvent.GameEvent("event_match_complete"))
|
||||
{
|
||||
e.AddContent("event_id", data.eventID)
|
||||
.AddContent("round_id", roundId)
|
||||
.AddContent("stage_id", stage.ID)
|
||||
.AddContent("item_consume", data.tokenConsumed);
|
||||
}
|
||||
#endif
|
||||
//领奖
|
||||
ChangeSource changeSource = new ChangeSource(data.eventID, "Event_Minigame1", "Event_Minigame1_Match", "StageReward");
|
||||
List<ItemData> itemData = GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(data.inflation, stage.DropId, changeSource);
|
||||
|
||||
//下一关
|
||||
data.index++;
|
||||
data.seed = new System.Random().Next();
|
||||
data.isOpenedList.Clear();
|
||||
data.swapFrom = -1;
|
||||
data.swapTo = -1;
|
||||
data.swapFrom2 = -1;
|
||||
data.swapTo2 = -1;
|
||||
data.tokenConsumed = 0;
|
||||
SaveData(data);
|
||||
if (stage.ID == eventMatchInit.StageListRound1[^1] || stage.ID == eventMatchInit.StageListRound2[^1])
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public List<int> GetBoardData(MatchGameData data)
|
||||
{
|
||||
EventMatchInit eventMatchInit = GetMatchInit(data.eventID);
|
||||
|
||||
EventMatchStage stage = GetMatchStage(data, eventMatchInit);
|
||||
int count = stage.ItemCount / 2;
|
||||
|
||||
List<EventMatchItem> eventMatchItems = tables.TbEventMatchItem.DataList;
|
||||
List<int> ids = new List<int>(eventMatchInit.EventItemList);
|
||||
List<int> itemList = new List<int>(stage.ItemList);
|
||||
List<int> itemID = new List<int>();
|
||||
int id;
|
||||
if (itemList.Count > 0)
|
||||
{
|
||||
count -= itemList.Count;
|
||||
for (int i = 0; i < itemList.Count; i++)
|
||||
{
|
||||
id = eventMatchInit.EventItemList[itemList[i] - 1];
|
||||
ids.Remove(id);
|
||||
EventMatchItem eventMatchItem = tables.TbEventMatchItem.GetOrDefault(id);
|
||||
if (eventMatchItem != null)
|
||||
{
|
||||
itemID.Add(id);
|
||||
itemID.Add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
System.Random random = new System.Random(data.seed);
|
||||
if (count > 0)
|
||||
{
|
||||
int index;
|
||||
double spRoll = random.NextDouble();
|
||||
if (spRoll < stage.SpItemProb)
|
||||
{
|
||||
var spItemIDs = eventMatchInit.EventSpecialItemList;
|
||||
if (spItemIDs.Count > 0)
|
||||
{
|
||||
count--;
|
||||
index = random.Next(spItemIDs.Count);
|
||||
itemID.Add(spItemIDs[index]);
|
||||
itemID.Add(spItemIDs[index]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
index = random.Next(ids.Count);
|
||||
id = ids[index];
|
||||
ids.Remove(id);
|
||||
|
||||
itemID.Add(id);
|
||||
itemID.Add(id);
|
||||
}
|
||||
}
|
||||
//itemID 打乱顺序
|
||||
if (data.index == 0)
|
||||
{
|
||||
itemID[2] = itemID[0];
|
||||
itemID[1] = itemID[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
itemID = ConvertTools.Shuffle(itemID, random);
|
||||
}
|
||||
Debug.Log("[EventMatch] ItemID " + string.Join(',', itemID));
|
||||
return itemID;
|
||||
}
|
||||
|
||||
public async void ShowFestPack(int eventID)
|
||||
{
|
||||
try
|
||||
{
|
||||
var init = GetMatchInit(eventID);
|
||||
if (init == null)
|
||||
return;
|
||||
var packData = new EventNeoNormalPackData
|
||||
{
|
||||
EventId = eventID,
|
||||
PackId = init.PackId,
|
||||
// Panel = init.PackPanel
|
||||
};
|
||||
await EventNeoNormalPackPanel.TryShowAsync(packData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
}
|
||||
public EventMatchInit GetMatchInit(int eventID)
|
||||
{
|
||||
try
|
||||
{
|
||||
FishingEvent fishingEvent = tbFishingEvent.GetOrDefault(eventID);
|
||||
FishingEventCycleItem cycleItem = tbCycleItem.GetOrDefault(fishingEvent.RedirectID);
|
||||
return tbEventMatchInit[cycleItem.RedirectID];
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public EventMatchInit GetMatchInitRedirectID(int redirectID)
|
||||
{
|
||||
try
|
||||
{
|
||||
FishingEventCycleItem cycleItem = tbCycleItem.GetOrDefault(redirectID);
|
||||
return tbEventMatchInit[cycleItem.RedirectID];
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public EventMatchStage GetMatchStage(MatchGameData data, EventMatchInit eventMatchInit)
|
||||
{
|
||||
int stageID;
|
||||
int countRound1 = eventMatchInit.StageListRound1.Count;
|
||||
if (data.index < countRound1)
|
||||
{
|
||||
stageID = eventMatchInit.StageListRound1[data.index];
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = data.index - countRound1;
|
||||
index %= eventMatchInit.StageListRound2.Count;
|
||||
stageID = eventMatchInit.StageListRound2[index];
|
||||
}
|
||||
return tables.TbEventMatchStage.GetOrDefault(stageID);
|
||||
}
|
||||
|
||||
public void SaveData(MatchGameData matchGameData)
|
||||
{
|
||||
saveStore.Write(EventMatchGameDataKey, JsonMapper.ToJson(matchGameData)).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,518 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using Script.RuntimeScript;
|
||||
using Script.RuntimeScript.model.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
|
||||
public class DiggingGameModel
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
#region 协议Const区域
|
||||
|
||||
//工具个数
|
||||
private const string DigCountKey = "DigCount";
|
||||
//棋盘管卡数据
|
||||
private const string DigGridDataKey = "DigGridData";
|
||||
|
||||
#endregion
|
||||
|
||||
#region 管卡json中原始数据
|
||||
private DinggingSceneData _dinggingSceneData;
|
||||
#endregion
|
||||
|
||||
#region 服务器数据
|
||||
/// <summary>
|
||||
/// 沙滩寻宝基本数据
|
||||
/// </summary>
|
||||
private SandDigServerData _serverData = null;
|
||||
public int MaxCount = 10;
|
||||
/// <summary>
|
||||
/// 沙滩寻宝棋盘网格数据
|
||||
/// </summary>
|
||||
private List<ServerGridData> _allServerGridData = new List<ServerGridData>();
|
||||
private int _serverDigCount = 0;
|
||||
int redDot = 10;
|
||||
//最新获取 道具个数 *** 已经废弃
|
||||
public int NewAddDigCount = 0;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 本期活动开放的所有管卡
|
||||
/// </summary>
|
||||
private List<int> _allClamps = new List<int>();
|
||||
public List<int> AllClamps => _allClamps;
|
||||
|
||||
private int _curActivityId = 0;
|
||||
public int CurActivityId
|
||||
{
|
||||
get => _curActivityId;
|
||||
}
|
||||
public float InflationRate;
|
||||
public DiggingActivityInit digActivityConfig;
|
||||
public string ResourceTopic => digActivityConfig.ResourceTopic;
|
||||
#region 数据初始化
|
||||
|
||||
/// <summary>
|
||||
/// 在线时候 活动开启
|
||||
/// </summary>
|
||||
public void ActivityOpen(cfg.FishingEvent t)
|
||||
{
|
||||
if (_curActivityId == t.ID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_curActivityId = t.ID;
|
||||
if (_curActivityId > 0)
|
||||
{
|
||||
FishingEventCycleItem cycle = GContext.container.Resolve<Tables>().TbFishingEventCycleItem.GetOrDefault(t.RedirectID);
|
||||
digActivityConfig = GContext.container.Resolve<Tables>().TbDiggingActivityInit.Get(cycle.RedirectID);
|
||||
MaxCount = digActivityConfig.StageCount;
|
||||
redDot = cycle.RedDot;
|
||||
}
|
||||
DiggingGameManager.LogError("沙滩寻宝开启");
|
||||
string countJson = saveStore.Read(DigCountKey).ValueOrThrow();
|
||||
string digGridDataJson = saveStore.Read(DigGridDataKey).ValueOrThrow();
|
||||
if (string.IsNullOrEmpty(countJson))
|
||||
{
|
||||
SaveNull();
|
||||
}
|
||||
DoServerData(countJson, digGridDataJson);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 进入游戏初始化数据
|
||||
/// </summary>
|
||||
/// <param name="userDatas"></param>
|
||||
public async System.Threading.Tasks.Task InitServerData()
|
||||
{
|
||||
_curActivityId = GContext.container.Resolve<DiggingGameManager>().GetActivityId();
|
||||
DiggingGameManager.LogError("沙滩寻宝数据初始化");
|
||||
if (_curActivityId > 0)
|
||||
{
|
||||
var _tables = GContext.container.Resolve<Tables>();
|
||||
FishingEvent fishingEvent = _tables.TbFishingEvent.GetOrDefault(_curActivityId);
|
||||
FishingEventCycleItem cycle = _tables.TbFishingEventCycleItem.GetOrDefault(fishingEvent.RedirectID);
|
||||
|
||||
digActivityConfig = _tables.TbDiggingActivityInit.Get(cycle.RedirectID);
|
||||
MaxCount = digActivityConfig.StageCount;
|
||||
}
|
||||
|
||||
string countJson = saveStore.Read(DigCountKey).ValueOrThrow();
|
||||
string digGridDataJson = saveStore.Read(DigGridDataKey).ValueOrThrow();
|
||||
DoServerData(countJson, digGridDataJson);
|
||||
await InitClientData(ClientDataHandleEnum.Init);
|
||||
}
|
||||
private void DoServerData(string countJson, string digGridDataJson)
|
||||
{
|
||||
if (_curActivityId <= 0)
|
||||
{
|
||||
SaveNull();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var tables = GContext.container.Resolve<Tables>();
|
||||
|
||||
FishingEvent acInfo = GContext.container.Resolve<DiggingGameManager>().GetActivityConfig(_curActivityId);
|
||||
FishingEventCycleItem cycle = tables.TbFishingEventCycleItem.GetOrDefault(acInfo.RedirectID);
|
||||
int redirectID = cycle.RedirectID;
|
||||
redDot = cycle.RedDot;
|
||||
|
||||
DiggingActivityInit digActivityConfig = GContext.container.Resolve<Tables>().TbDiggingActivityInit.Get(redirectID);
|
||||
if (digActivityConfig == null)
|
||||
{
|
||||
SaveNull();
|
||||
return;
|
||||
}
|
||||
_allClamps = digActivityConfig.StageList;
|
||||
|
||||
if (!string.IsNullOrEmpty(countJson))
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveDigCountJson data = Newtonsoft.Json.JsonConvert.DeserializeObject<SaveDigCountJson>(countJson);
|
||||
if (data.ActivityId == _curActivityId)
|
||||
{
|
||||
_serverDigCount = data.DigCount;
|
||||
DiggingGameManager.LogError("玩家铲子个数 :::" + _serverDigCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveNull();
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
SaveNull();
|
||||
Debug.LogError(e);
|
||||
}
|
||||
}
|
||||
_serverData = null;
|
||||
_allServerGridData.Clear();
|
||||
if (!string.IsNullOrEmpty(digGridDataJson))
|
||||
{
|
||||
SaveDataJson data = Newtonsoft.Json.JsonConvert.DeserializeObject<SaveDataJson>(digGridDataJson);
|
||||
if (data.ActivityId == _curActivityId)
|
||||
{
|
||||
_serverData = data.GetSerializableData();
|
||||
_allServerGridData = data.GetSerializableGridData().ToList();
|
||||
InflationRate = data.InflationRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveNull();
|
||||
}
|
||||
}
|
||||
if (_serverData == null || (!_allClamps.Contains(_serverData.ClampId) && _serverData.ClampId > 0))
|
||||
{
|
||||
InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
_serverData = new SandDigServerData();
|
||||
_serverData.Index = 0;
|
||||
_allServerGridData.Clear();
|
||||
}
|
||||
int index = _serverData.Index % MaxCount;
|
||||
_serverData.ClampId = _allClamps[index];
|
||||
SetRedPoint();
|
||||
//#if UNITY_EDITOR
|
||||
// //测试
|
||||
// _allServerGridData = new List<ServerGridData>();
|
||||
// _allServerGridPropsData = new List<ServerGridPropsData>();
|
||||
// _serverData = new SandDigServerData();
|
||||
// _serverData.ClampId = _allClamps[1];
|
||||
//#endif
|
||||
}
|
||||
}
|
||||
public void GMSetClamp(int index)
|
||||
{
|
||||
if (_curActivityId <= 0 || _serverData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
_serverData.Index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_serverData.Index = index;
|
||||
}
|
||||
NewData();
|
||||
}
|
||||
public async Task<bool> InitClientData(ClientDataHandleEnum handleType)
|
||||
{
|
||||
if (_serverData == null || _serverData.ClampId <= 0)
|
||||
return false;
|
||||
DiggingGameManager.LogError("初始化客户端管卡数据");
|
||||
DiggingStageReward config = GContext.container.Resolve<Tables>().TbDiggingStageReward.Get(_serverData.ClampId);
|
||||
string JsonPath = config.StageName;// $"Assets/ABPackage/SandDig/ClampJson/Clamp{_serverData.ClampId}";
|
||||
|
||||
GContext.container.Resolve<DiggingGameManager>().GameState = SandDigGameState.LoadJson;
|
||||
AsyncOperationHandle<TextAsset> handle = Addressables.LoadAssetAsync<TextAsset>(JsonPath);
|
||||
TaskCompletionSource<bool> taskCompletion = new TaskCompletionSource<bool>();
|
||||
handle.Completed += (AsyncOperationHandle<TextAsset> obj) =>
|
||||
{
|
||||
if (obj.Status == AsyncOperationStatus.Succeeded)
|
||||
{
|
||||
DiggingGameManager.LogError("新管卡数据加载完毕");
|
||||
_dinggingSceneData = JsonConvert.DeserializeObject<DinggingSceneData>(obj.Result.ToString());
|
||||
_dinggingSceneData.AnalysisData(_allServerGridData);
|
||||
// 释放资源
|
||||
Addressables.Release(handle);
|
||||
if (handleType == ClientDataHandleEnum.PassClamp)
|
||||
{
|
||||
//可以切换了
|
||||
GContext.container.Resolve<DiggingGameManager>().GameState = SandDigGameState.WaitPassLoading;
|
||||
}
|
||||
else if (handleType == ClientDataHandleEnum.EnterGame)
|
||||
{
|
||||
GContext.container.Resolve<DiggingGameManager>().GameState = SandDigGameState.LoadJsonOver;
|
||||
}
|
||||
else if (handleType == ClientDataHandleEnum.Init)
|
||||
{
|
||||
InitCheckClampPass();
|
||||
}
|
||||
taskCompletion.SetResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletion.SetResult(false);
|
||||
DiggingGameManager.LogError("Failed to load asset: " + JsonPath);
|
||||
}
|
||||
};
|
||||
return await taskCompletion.Task;
|
||||
}
|
||||
public void InitCheckClampPass()
|
||||
{
|
||||
List<int> allHaveProps = new List<int>(GetAllPassIds());
|
||||
PropData[] allData = GetAllPropData();
|
||||
bool allFinded = true;
|
||||
|
||||
for (int i = 0; i < allData.Length; i++)
|
||||
{
|
||||
var propData = allData[i];
|
||||
if (!allHaveProps.Contains(propData.PropId))
|
||||
{
|
||||
allFinded = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
allHaveProps.Remove(propData.PropId);
|
||||
}
|
||||
}
|
||||
if (!allFinded)
|
||||
{
|
||||
allFinded = true;
|
||||
GridData[] gridDatas = GetAllGridData();
|
||||
for (int i = 0; i < gridDatas.Length; i++)
|
||||
{
|
||||
GridData gridData = gridDatas[i];
|
||||
if (gridData.IsBorder || gridData.IsOpen)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
allFinded = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allFinded)
|
||||
{
|
||||
//DiggingStageReward config = GContext.container.Resolve<Tables>().TbDiggingStageReward.Get(_serverData.ClampId);
|
||||
_serverData.Index++;
|
||||
NewData();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 通过当前管卡发奖
|
||||
/// </summary>
|
||||
public void PassClamp()
|
||||
{
|
||||
GContext.container.Resolve<DiggingGameManager>().ClampIsPass = true;
|
||||
DiggingStageReward config = GContext.container.Resolve<Tables>().TbDiggingStageReward.Get(_serverData.ClampId);
|
||||
|
||||
var playerData = GContext.container.Resolve<PlayerItemData>();
|
||||
|
||||
DiggingGameManager.LogError("获得的Drop奖励 :::" + config.DropID);
|
||||
ChangeSource changeSource = new ChangeSource(CurActivityId, "Event_Minigame1", "Event_Minigame1_Dig", "StageReward");
|
||||
|
||||
GContext.container.Resolve<DiggingGameManager>().itemDatas = playerData.AddItemByDropLureInflation(InflationRate, config.DropID, changeSource);
|
||||
|
||||
_serverData.Index++;
|
||||
|
||||
//if (_serverData.Index >= MaxCount && _serverData.Index % MaxCount == 0)
|
||||
//{
|
||||
// GContext.container.Resolve<DiggingGameManager>().itemDatas = playerData.AddItemByDropLureInflation(InflationRate, digActivityConfig.Reward);
|
||||
//}
|
||||
NewData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在线进入新关卡,数据初始化
|
||||
/// </summary>
|
||||
public void NewData()
|
||||
{
|
||||
int index = _serverData.Index % MaxCount;
|
||||
_serverData.ClampId = _allClamps[index];
|
||||
_serverData.ClickCount = 0;
|
||||
_serverData.Reset();
|
||||
_allServerGridData.Clear();
|
||||
_dinggingSceneData?.Reset();
|
||||
SaveAll();
|
||||
}
|
||||
public void NewClampInit()
|
||||
{
|
||||
GContext.Publish(new EventSandDigNewClamp(_serverData.ClampId));
|
||||
if (_serverData.ClampId <= 0)
|
||||
{
|
||||
//通关所有关卡
|
||||
DiggingGameManager.LogError("通关所有关卡!!!!!!!");
|
||||
}
|
||||
else
|
||||
{
|
||||
InitClientData(ClientDataHandleEnum.PassClamp);
|
||||
}
|
||||
}
|
||||
|
||||
#region 数据接口
|
||||
|
||||
/// <summary>
|
||||
/// 使用道具
|
||||
/// </summary>
|
||||
public void UseDig()
|
||||
{
|
||||
_serverDigCount--;
|
||||
_serverData.ClickCount++;
|
||||
SaveDigCount();
|
||||
|
||||
GContext.Publish(new EventSandDigPropUpdate());
|
||||
}
|
||||
/// <summary>
|
||||
/// 增加道具
|
||||
/// </summary>
|
||||
/// <param name="count"></param>
|
||||
public void AddDigCount(int count)
|
||||
{
|
||||
_serverDigCount += count;
|
||||
GContext.Publish(new EventSandDigPropUpdate());
|
||||
SaveDigCount();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获得目标道具
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
public void AddProp(int id)
|
||||
{
|
||||
_serverData.PassPropIds.Add(id);
|
||||
}
|
||||
|
||||
public List<int> GetAllPassIds()
|
||||
{
|
||||
return _serverData.PassPropIds;
|
||||
}
|
||||
|
||||
public DinggingSceneData GetBroadData()
|
||||
{
|
||||
return _dinggingSceneData;
|
||||
}
|
||||
|
||||
public BroadData[] GetAllBroadData()
|
||||
{
|
||||
return _dinggingSceneData.GetAllBroadData();
|
||||
}
|
||||
|
||||
public GridData[] GetAllGridData()
|
||||
{
|
||||
return _dinggingSceneData.GetAllGridData();
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取一个格子数据
|
||||
/// </summary>
|
||||
/// <param name="row"></param>
|
||||
/// <param name="col"></param>
|
||||
/// <returns></returns>
|
||||
public GridData GetGridData(int row, int col)
|
||||
{
|
||||
return _dinggingSceneData.GetGridData(row, col);
|
||||
}
|
||||
|
||||
public PropData[] GetAllPropData()
|
||||
{
|
||||
return _dinggingSceneData.GetAllPropData();
|
||||
}
|
||||
|
||||
public List<PropTempData> GetOnePropTempData()
|
||||
{
|
||||
int index = _serverData.Index / MaxCount;
|
||||
return _dinggingSceneData.GetAllPropTempData(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取道具个数
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int GetDigCount()
|
||||
{
|
||||
return _serverDigCount;
|
||||
}
|
||||
|
||||
public int CurIndex()
|
||||
{
|
||||
return _serverData.Index;
|
||||
}
|
||||
public SandDigServerData GetServerData()
|
||||
{
|
||||
return _serverData;
|
||||
}
|
||||
|
||||
public int GetCurClampId()
|
||||
{
|
||||
return _serverData.ClampId;
|
||||
}
|
||||
public int GetClickCount()
|
||||
{
|
||||
return _serverData.ClickCount;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 数据存储
|
||||
|
||||
public void SaveDigCount()
|
||||
{
|
||||
SetRedPoint();
|
||||
SaveDigCountJson dataJson = new SaveDigCountJson();
|
||||
dataJson.ActivityId = _curActivityId;
|
||||
dataJson.DigCount = _serverDigCount;
|
||||
string json = JsonConvert.SerializeObject(dataJson);
|
||||
saveStore.Write(DigCountKey, json).RequireAccepted();
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(dataJson.ActivityId, dataJson.DigCount);
|
||||
}
|
||||
void SetRedPoint()
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState("Home.Digging", _serverData != null && _serverDigCount > 0, _serverDigCount);
|
||||
}
|
||||
public void SaveData()
|
||||
{
|
||||
//通用数据 + 任务目标数据
|
||||
SaveDataJson dataJson = new SaveDataJson();
|
||||
dataJson.ActivityId = _curActivityId;
|
||||
dataJson.SandDigServerData = _serverData.ToJson();
|
||||
dataJson.InflationRate = InflationRate;
|
||||
//网格数据
|
||||
GridData[] allGridData = _dinggingSceneData?.GetAllGridData() ?? new GridData[0];
|
||||
string allGridStr = allGridData.Length > 0 ? "[" : string.Empty;
|
||||
|
||||
for (int i = 0; i < allGridData.Length; i++)
|
||||
{
|
||||
if (i == allGridData.Length - 1)
|
||||
{
|
||||
allGridStr += allGridData[i].ToJson() + "]";
|
||||
}
|
||||
else
|
||||
{
|
||||
allGridStr += allGridData[i].ToJson() + ",";
|
||||
}
|
||||
}
|
||||
dataJson.AllGridJson = allGridStr;
|
||||
|
||||
string json = JsonConvert.SerializeObject(dataJson);
|
||||
//DiggingGameManager.LogError(json);
|
||||
saveStore.Write(DigGridDataKey, json).RequireAccepted();
|
||||
}
|
||||
|
||||
public void SaveAll()
|
||||
{
|
||||
this.SaveDigCount();
|
||||
this.SaveData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空上一期活动数据
|
||||
/// </summary>
|
||||
private void SaveNull()
|
||||
{
|
||||
_serverDigCount = GContext.container.Resolve<FishingEventData>().GetInitWelcomeGift(_curActivityId);
|
||||
SaveDigCount();
|
||||
saveStore.Write(DigGridDataKey, string.Empty).RequireAccepted();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,706 @@
|
||||
using asap.core;
|
||||
using com.fpnn;
|
||||
using com.fpnn.rtm;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UniRx;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class ChatQuestProcessor : RTMQuestProcessor
|
||||
{
|
||||
public Func<bool> CanRelogin;
|
||||
public override void SessionClosed(int ClosedByErrorCode)
|
||||
{
|
||||
Debug.Log("SessionClosed errorCode = " + ClosedByErrorCode);
|
||||
}
|
||||
|
||||
public override bool ReloginWillStart(int lastErrorCode, int retriedCount)
|
||||
{
|
||||
Debug.Log("ReloginWillStart lastErrorCode = " + lastErrorCode + ", retriedCount = " + retriedCount);
|
||||
return CanRelogin?.Invoke() == true;
|
||||
}
|
||||
|
||||
public override void Kickout()
|
||||
{
|
||||
Debug.Log("Kickout");
|
||||
}
|
||||
|
||||
public override void KickoutRoom(long roomId)
|
||||
{
|
||||
Debug.Log("KickoutRoom roomId = " + roomId);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ChatMessage : IComparable<ChatMessage>
|
||||
{
|
||||
public ChatMessage(long messageId, long fromUid, string message, long mtime, long cursorId = 0)
|
||||
{
|
||||
this.messageId = messageId;
|
||||
this.fromUid = fromUid;
|
||||
this.message = message;
|
||||
this.mtime = mtime;
|
||||
this.cursorId = cursorId;
|
||||
}
|
||||
public static ChatMessage ConvertoFromRTMMessage(RTMMessage message)
|
||||
{
|
||||
ChatMessage chatDemoMessage = new ChatMessage(message.messageId, message.fromUid, message.stringMessage, message.modifiedTime);
|
||||
return chatDemoMessage;
|
||||
}
|
||||
|
||||
public static ChatMessage ConvertoFromHistoryMessage(HistoryMessage message)
|
||||
{
|
||||
ChatMessage chatDemoMessage = new ChatMessage(message.messageId, message.fromUid, message.stringMessage, message.modifiedTime, message.cursorId);
|
||||
return chatDemoMessage;
|
||||
}
|
||||
|
||||
public int CompareTo(ChatMessage message)
|
||||
{
|
||||
if (mtime > message.mtime)
|
||||
return 1;
|
||||
else if (mtime < message.mtime)
|
||||
return -1;
|
||||
else
|
||||
{
|
||||
if (messageId >= message.messageId)
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
public long messageId;
|
||||
public long fromUid;
|
||||
public string message;
|
||||
public long mtime;
|
||||
public long cursorId;
|
||||
}
|
||||
|
||||
public class ChatMessageCompare : IEqualityComparer<ChatMessage>
|
||||
{
|
||||
public bool Equals(ChatMessage msg1, ChatMessage msg2)
|
||||
{
|
||||
if (msg1.mtime == msg2.mtime && msg1.messageId == msg2.messageId)
|
||||
{
|
||||
if (msg1.cursorId == msg2.cursorId)
|
||||
return true;
|
||||
else if ((msg1.cursorId == 0 && msg2.cursorId != 0) || (msg1.cursorId != 0 && msg2.cursorId == 0))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public int GetHashCode(ChatMessage msg)
|
||||
{
|
||||
if (msg == null)
|
||||
return 0;
|
||||
else
|
||||
return string.Format("{0}_{1}_{2}", msg.mtime, msg.messageId, msg.message).GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ChatConversation
|
||||
{
|
||||
//public Int32 unreadCount;
|
||||
public Int64 lastMessageTime;
|
||||
//public ChatMessage lastMessage;
|
||||
|
||||
//public void AddMessage(ChatMessage message)
|
||||
//{
|
||||
// if (lastMessage == null)
|
||||
// {
|
||||
// lastMessage = message;
|
||||
// return;
|
||||
// }
|
||||
// if (message.mtime > lastMessage.mtime)
|
||||
// {
|
||||
// lastMessage = message;
|
||||
// return;
|
||||
// }
|
||||
// else if (message.mtime == lastMessage.mtime && lastMessage.messageId > message.messageId)
|
||||
// {
|
||||
// lastMessage = message;
|
||||
// return;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
public class ChatChatData
|
||||
{
|
||||
public Int64 lastId;
|
||||
public List<ChatMessage> messageList;
|
||||
}
|
||||
|
||||
public class ChatChangeEvent
|
||||
{
|
||||
public bool nextContent;
|
||||
public bool push;
|
||||
}
|
||||
|
||||
public enum ChatType
|
||||
{
|
||||
Text = 1,//文本消息
|
||||
System = 2,//系统内提示
|
||||
System2 = 3,//解锁地图
|
||||
Help = 4,//请求照片
|
||||
Emoji = 5,//发送表情
|
||||
HelpEnergy = 6//请求体力帮助
|
||||
}
|
||||
|
||||
public class ChatService : IChatService,IDisposable
|
||||
{
|
||||
private IUserService userService { get; set; }
|
||||
private IConfig config { get; set; }
|
||||
private int sendInterval = 2;
|
||||
long groupConversationId = 0;
|
||||
RTMClient client;
|
||||
ChatConversation chatConversation;
|
||||
ChatChatData chat;
|
||||
readonly CancellationToken loginLifetime;
|
||||
readonly CancellationTokenRegistration closeOnLogout;
|
||||
bool disposed;
|
||||
bool loginStarted;
|
||||
bool IsCurrent => !disposed && !loginLifetime.IsCancellationRequested && userService.IsAuthorized;
|
||||
|
||||
public ChatService(IUserService userService, IConfig config, cfg.Tables tables)
|
||||
{
|
||||
this.userService = userService;
|
||||
loginLifetime = userService.LoginLifetime;
|
||||
closeOnLogout = loginLifetime.Register(() => client?.Close());
|
||||
this.config = config;
|
||||
this.sendInterval = tables.TbServerConfig.ClubChatLimitTime;
|
||||
ClientEngine.Init();
|
||||
RTMControlCenter.Init();
|
||||
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
disposed = true;
|
||||
disposable?.Dispose();
|
||||
closeOnLogout.Dispose();
|
||||
client?.Close();
|
||||
client = null;
|
||||
}
|
||||
|
||||
private IDisposable disposable;
|
||||
public void WaitingForLogin()
|
||||
{
|
||||
if (disposed || loginLifetime.IsCancellationRequested || loginStarted || disposable != null) return;
|
||||
disposable = RootCtx.OnEvent<EvtAPISvrLoginSuccess>().Subscribe(OnApiSvrLoginSuccess);
|
||||
if (userService.IsAuthorized) OnApiSvrLoginSuccess(default);
|
||||
}
|
||||
|
||||
private async void OnApiSvrLoginSuccess(EvtAPISvrLoginSuccess evt)
|
||||
{
|
||||
if (!IsCurrent || loginStarted) return;
|
||||
loginStarted = true;
|
||||
disposable?.Dispose();
|
||||
disposable = null;
|
||||
try
|
||||
{
|
||||
int retryCount = 0;
|
||||
while (IsCurrent && !await LoginRTM())
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
retryCount++;
|
||||
await Task.Delay(Math.Min(5000 + retryCount * 1000, 10000), loginLifetime);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (loginLifetime.IsCancellationRequested) { }
|
||||
catch (Exception ex) { if (IsCurrent) Debug.LogException(ex); }
|
||||
}
|
||||
|
||||
private async Task<bool> LoginRTM(int timeout = 5)
|
||||
{
|
||||
if (!IsCurrent) return false;
|
||||
var userIdLong = userService.InternalId;
|
||||
var pid = long.Parse(config.Get(GConstant.K_RTMP_ID, "80000560"));
|
||||
var rtmServerEndpoint = config.Get(GConstant.K_RTM_Server_Endpoint, "rtm-nx-front.ilivedata.com:13321");
|
||||
var hmacSecret = config.Get(GConstant.K_RTM_Hmac_Secret, "7a1ba6bb85a4473d96fa4bf45b01f1dd");
|
||||
ChatQuestProcessor processor = new ChatQuestProcessor();
|
||||
InitProcessor(processor);
|
||||
client = RTMClient.getInstance(rtmServerEndpoint, pid, userIdLong, processor);
|
||||
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
using var cancelled = loginLifetime.Register(() => taskCompletionSource.TrySetCanceled());
|
||||
|
||||
string token = GetToken(pid, hmacSecret, userIdLong, out long ts);
|
||||
|
||||
client.Login((long pid, long uid, bool successful, int errorCode) =>
|
||||
{
|
||||
if (!IsCurrent) { taskCompletionSource.TrySetResult(false); return; }
|
||||
if (successful)
|
||||
{
|
||||
taskCompletionSource.TrySetResult(true);
|
||||
//RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
//{
|
||||
// GetConversationList();
|
||||
//});
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletionSource.TrySetResult(false);
|
||||
Debug.Log("Login failed, errorCode = " + errorCode);
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("ilivedata"))
|
||||
{
|
||||
e.AddContent("connection_failure", errorCode)
|
||||
.AddContent("pid", pid)
|
||||
.AddContent("uid", uid)
|
||||
.AddContent("userIdLong", userIdLong);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}, token, ts, timeout);
|
||||
|
||||
return await (taskCompletionSource.Task);
|
||||
}
|
||||
|
||||
string GetToken(long pid, string hmacSecret, long uid, out long ts)
|
||||
{
|
||||
ts = ClientEngine.GetCurrentSeconds();
|
||||
string text = pid.ToString() + ":" + uid.ToString() + ":" + ts.ToString();
|
||||
|
||||
using (var hmacsha256 = new HMACSHA256(Convert.FromBase64String(hmacSecret)))
|
||||
{
|
||||
var hash = hmacsha256.ComputeHash(Encoding.UTF8.GetBytes(text));
|
||||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<HashSet<long>> GetOnlineUsers(List<long> playerIDs)
|
||||
{
|
||||
HashSet<long> longs = new HashSet<long>();
|
||||
|
||||
if (!IsCurrent || client?.Status != RTMClient.ClientStatus.Connected)
|
||||
return longs;
|
||||
|
||||
var taskCompletionSource = new TaskCompletionSource<HashSet<long>>();
|
||||
using var cancelled = loginLifetime.Register(() => taskCompletionSource.TrySetResult(null));
|
||||
longs = new HashSet<long>(playerIDs);
|
||||
client.GetOnlineUsers((HashSet<long> uids, int errorCode) =>
|
||||
{
|
||||
if (IsCurrent && errorCode == 0)
|
||||
{
|
||||
taskCompletionSource.TrySetResult(uids);
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletionSource.TrySetResult(null);
|
||||
}
|
||||
}, longs);
|
||||
return await taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
public void GetConversationList(long groupConversationId)
|
||||
{
|
||||
this.groupConversationId = groupConversationId;
|
||||
|
||||
if (!IsCurrent || client?.Status != RTMClient.ClientStatus.Connected)
|
||||
return;
|
||||
|
||||
RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
GetConversationList();
|
||||
});
|
||||
}
|
||||
|
||||
void InitProcessor(ChatQuestProcessor processor)
|
||||
{
|
||||
processor.CanRelogin = () => IsCurrent;
|
||||
processor.PushGroupChatCallback = OnPushGroupChat;
|
||||
processor.PushGroupMessageCallback = OnPushGroupMessage;
|
||||
//processor.PushMessageCallback = OnPushGroupMessage;
|
||||
//processor.PushChatCallback = OnPushGroupChat;
|
||||
processor.ReloginCompletedCallback = OnRelogin;
|
||||
}
|
||||
|
||||
void OnPushGroupChat(RTMMessage message)
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
if (message.toId == groupConversationId)
|
||||
{
|
||||
AddMessage(groupConversationId, true);
|
||||
}
|
||||
//else if (message.toId == userIdLong)
|
||||
//{
|
||||
// //个人聊天列表
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
void OnPushGroupMessage(RTMMessage message)
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
if (message.toId == groupConversationId)
|
||||
{
|
||||
//主界面推送消息
|
||||
|
||||
}
|
||||
//else if (message.toId == userIdLong)
|
||||
//{
|
||||
// //个人聊天列表
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
void OnRelogin(bool successful, bool retryAgain, int errorCode, int retriedCount)
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
//Debug.Log("OnRelogin successful = " + successful + " retryAgain = " + retryAgain + ", errorCode = " + errorCode + ", retriedCount = " + retriedCount);
|
||||
if (successful && chatConversation != null)
|
||||
{
|
||||
client?.GetGroupUnreadConversationList((List<Conversation> groupConversationList, int errorCode) =>
|
||||
{
|
||||
if (IsCurrent && errorCode == 0)
|
||||
{
|
||||
InitConversationList(groupConversationList);
|
||||
}
|
||||
}, startTime: chatConversation.lastMessageTime);
|
||||
}
|
||||
}
|
||||
|
||||
void GetConversationList()
|
||||
{
|
||||
if (!IsCurrent || client == null) return;
|
||||
long uid = client.Uid;
|
||||
client?.GetGroupConversationList((List<Conversation> groupConversationList, int errorCode) =>
|
||||
{
|
||||
if (IsCurrent && errorCode == 0)
|
||||
{
|
||||
InitConversationList(groupConversationList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void InitConversationList(List<Conversation> list)
|
||||
{
|
||||
RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
foreach (Conversation conversation in list)
|
||||
{
|
||||
if (conversation.id == groupConversationId)
|
||||
{
|
||||
if (chatConversation == null)
|
||||
{
|
||||
chatConversation = new ChatConversation();
|
||||
}
|
||||
//chatConversation.unreadCount += conversation.unreadCount;
|
||||
//if (conversation.lastMessage != null && conversation.lastMessage.messageId != 0)
|
||||
// chatConversation.AddMessage(ChatMessage.ConvertoFromHistoryMessage(conversation.lastMessage));
|
||||
UpdateConversationList(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateConversationList(bool push)
|
||||
{
|
||||
if (chatConversation == null)
|
||||
return;
|
||||
//client?.GetGroupMessage((int count, long lastCursorId, long beginMsec, long endMsec, List<HistoryMessage> messages, int errorCode) =>
|
||||
//{
|
||||
// if (IsCurrent && errorCode == 0)
|
||||
// {
|
||||
// InitChat(lastCursorId, messages);
|
||||
// }
|
||||
//}, groupConversationId, true, 10);
|
||||
//两个接口一样 GetGroupMessage多一个指定类型
|
||||
client?.GetGroupChat((int count, long lastCursorId, long beginMsec, long endMsec, List<HistoryMessage> messages, int errorCode) =>
|
||||
{
|
||||
if (IsCurrent && errorCode == 0)
|
||||
{
|
||||
InitChat(lastCursorId, messages, push, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("GetGroupChat errorCode = " + errorCode);
|
||||
}
|
||||
}, groupConversationId, true, 10);
|
||||
}
|
||||
|
||||
void InitChat(long lastId, List<HistoryMessage> messages, bool push, bool nextContent)
|
||||
{
|
||||
RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
if (chat == null)
|
||||
{
|
||||
chat = new ChatChatData();
|
||||
chat.messageList = new List<ChatMessage>();
|
||||
}
|
||||
if (messages != null)
|
||||
{
|
||||
if ((chat.lastId == 0 || lastId < chat.lastId) && lastId != 0)
|
||||
chat.lastId = lastId;
|
||||
|
||||
foreach (HistoryMessage message in messages)
|
||||
{
|
||||
chat.messageList.Add(ChatMessage.ConvertoFromHistoryMessage(message));
|
||||
}
|
||||
chat.messageList = chat.messageList.Distinct(new ChatMessageCompare()).ToList();
|
||||
chat.messageList.Sort();
|
||||
GContext.Publish(new ChatChangeEvent() { nextContent = nextContent, push = push });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async void AddMessage(long conversationId, bool push)
|
||||
{
|
||||
await Task.Delay(100);//服务器聊天记录存储延时
|
||||
RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
if (conversationId == groupConversationId)
|
||||
{
|
||||
if (chatConversation == null)
|
||||
{
|
||||
chatConversation = new ChatConversation();
|
||||
//chatConversation.unreadCount = 1;
|
||||
//chatConversation.lastMessage = message;
|
||||
}
|
||||
//else
|
||||
//{
|
||||
// chatConversation.AddMessage(message);
|
||||
//}
|
||||
UpdateConversationList(push);
|
||||
//if (chat == null)
|
||||
// return;
|
||||
//chat.messageList.Add(message);
|
||||
//chat.messageList = chat.messageList.Distinct(new ChatMessageCompare()).ToList();
|
||||
//chat.messageList.Sort();
|
||||
//GContext.Publish(new ChatChangeEvent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async Task<long> CanSend(string message)
|
||||
{
|
||||
if (!IsCurrent || client == null || groupConversationId == 0)
|
||||
return 0;
|
||||
var taskCompletionSource = new TaskCompletionSource<long>();
|
||||
using var cancelled = loginLifetime.Register(() => taskCompletionSource.TrySetResult(0));
|
||||
message = string.Format("{0}#{1}", userService.UserId, message);
|
||||
client?.SendGroupChat((long messageId, long mtime, int errorCode) =>
|
||||
{
|
||||
if (!IsCurrent) { taskCompletionSource.TrySetResult(0); return; }
|
||||
if (errorCode != 0)
|
||||
{
|
||||
taskCompletionSource.TrySetResult(0);
|
||||
Debug.Log("SendGroupChat errorCode = " + errorCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletionSource.TrySetResult(messageId);
|
||||
RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
AddMessage(groupConversationId, false);
|
||||
if (chatConversation != null) chatConversation.lastMessageTime = mtime;
|
||||
});
|
||||
}
|
||||
}, groupConversationId, message);
|
||||
long messageId = await taskCompletionSource.Task;
|
||||
return messageId;
|
||||
}
|
||||
|
||||
async Task<long> Send(string message)
|
||||
{
|
||||
if (!IsCurrent || client == null || groupConversationId == 0)
|
||||
return 0;
|
||||
|
||||
if (!CanSend())
|
||||
{
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_42"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
var taskCompletionSource = new TaskCompletionSource<long>();
|
||||
using var cancelled = loginLifetime.Register(() => taskCompletionSource.TrySetResult(0));
|
||||
message = string.Format("{0}#{1}", userService.UserId, message);
|
||||
client?.SendGroupChat((long messageId, long mtime, int errorCode) =>
|
||||
{
|
||||
if (!IsCurrent) { taskCompletionSource.TrySetResult(0); return; }
|
||||
if (errorCode != 0)
|
||||
{
|
||||
taskCompletionSource.TrySetResult(0);
|
||||
Debug.Log("SendGroupChat errorCode = " + errorCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
taskCompletionSource.TrySetResult(messageId);
|
||||
RTMControlCenter.callbackQueue.PostAction(() =>
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
AddMessage(groupConversationId, false);
|
||||
if (chatConversation != null) chatConversation.lastMessageTime = mtime;
|
||||
});
|
||||
}
|
||||
}, groupConversationId, message);
|
||||
long messageId = await taskCompletionSource.Task;
|
||||
return messageId;
|
||||
}
|
||||
|
||||
Task sendTask = null;
|
||||
private bool CanSend()
|
||||
{
|
||||
if (sendTask == null || sendTask.IsCompleted)
|
||||
{
|
||||
sendTask = Task.Delay(TimeSpan.FromSeconds(sendInterval));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void NextPage()
|
||||
{
|
||||
if (!IsCurrent || client == null || groupConversationId == 0)
|
||||
return;
|
||||
if (chat == null)
|
||||
return;
|
||||
if (chat.messageList.Count >= GContext.container.Resolve<cfg.Tables>().TbServerConfig.ClubChatMaxCount)
|
||||
{
|
||||
return;
|
||||
}
|
||||
client?.GetGroupChat((int count, long lastCursorId, long beginMsec, long endMsec, List<HistoryMessage> messages, int errorCode) =>
|
||||
{
|
||||
if (IsCurrent && errorCode == 0)
|
||||
{
|
||||
InitChat(lastCursorId, messages, false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("GetGroupChat errorCode = " + errorCode);
|
||||
}
|
||||
}, groupConversationId, true, 10, lastId: chat.lastId);
|
||||
}
|
||||
|
||||
public void OnQuit()
|
||||
{
|
||||
chat = null;
|
||||
chatConversation = null;
|
||||
groupConversationId = 0;
|
||||
}
|
||||
|
||||
public void InitChatData()
|
||||
{
|
||||
chat = null;
|
||||
}
|
||||
|
||||
public ChatChatData GetGroupChat()
|
||||
{
|
||||
return chat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析消息类型
|
||||
/// </summary>
|
||||
/// <param name="chat"></param>
|
||||
/// <returns>1.文本消息 2.系统消息(没有点赞)3.系统消息 4.求助帖 5.图片</returns>
|
||||
public string[] ParseChat(string chat)
|
||||
{
|
||||
return chat.Split('#');
|
||||
}
|
||||
|
||||
public async Task<long> SendHelpEnergyMessage(int itemId)
|
||||
{
|
||||
string chat = $"{(int)ChatType.HelpEnergy}#{itemId}";
|
||||
return await CanSend(chat);
|
||||
}
|
||||
|
||||
public void SendEmoji(string chat)
|
||||
{
|
||||
chat = $"{(int)ChatType.Emoji}#{chat}";
|
||||
_ = Send(chat);
|
||||
}
|
||||
|
||||
public async Task<long> SendGetPhotoMessage(string chat)
|
||||
{
|
||||
chat = $"{(int)ChatType.Help}#{chat}";
|
||||
return await CanSend(chat);
|
||||
}
|
||||
|
||||
//谁干了什么
|
||||
public void SendSystemMessage2(string chat)
|
||||
{
|
||||
chat = $"{(int)ChatType.System2}#{chat}";
|
||||
_ = CanSend(chat);
|
||||
}
|
||||
|
||||
//俱乐部系统提示
|
||||
public void SendSystemMessage(string chat)
|
||||
{
|
||||
chat = $"{(int)ChatType.System}#{chat}";
|
||||
_ = CanSend(chat);
|
||||
}
|
||||
|
||||
public async void SendTxTMessage(string chat)
|
||||
{
|
||||
//审核
|
||||
bool value = await TextAudit(chat);
|
||||
if (!IsCurrent) return;
|
||||
if (value)
|
||||
{
|
||||
string message = $"{(int)ChatType.Text}#{chat}";
|
||||
var messageId = await Send(message);
|
||||
#if AGG
|
||||
if (messageId > 0)
|
||||
{
|
||||
await Awaiters.NextFrame;
|
||||
if (!IsCurrent) return;
|
||||
using (var e = GEvent.GameEvent("chat_msg"))
|
||||
{
|
||||
e.AddContent("content", chat);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_33"));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> TextAudit(string text)
|
||||
{
|
||||
if (!IsCurrent || client == null) return false;
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
using var cancelled = loginLifetime.Register(() => taskCompletionSource.TrySetResult(false));
|
||||
client.TextCheck((TextCheckResult result, int errorCode) =>
|
||||
{
|
||||
if (!IsCurrent) { taskCompletionSource.TrySetResult(false); return; }
|
||||
if (errorCode != com.fpnn.ErrorCode.FPNN_EC_OK)
|
||||
{
|
||||
taskCompletionSource.TrySetResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug.Log(" -- result " + result.result);
|
||||
|
||||
//if (result.tags != null)
|
||||
// Debug.Log(" -- tags.Count " + result.tags.Count);
|
||||
//if (result.wlist != null)
|
||||
// Debug.Log(" -- wlist.Count " + result.wlist.Count);
|
||||
taskCompletionSource.TrySetResult(result.result == 0);
|
||||
}
|
||||
}, text);
|
||||
return await taskCompletionSource.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,230 @@
|
||||
using System.Collections.Generic;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using UniRx;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
public interface IDeferredRewardStashService
|
||||
{
|
||||
public void DisposeSubscriptions();
|
||||
public void Reset();
|
||||
public void Flush(RewardType rewardType = RewardType.FishBoxFly, bool isShowReward = true);
|
||||
public void UploadData();
|
||||
public void LoadData();
|
||||
public IReadOnlyList<ItemData> Items { get; }
|
||||
public bool IsEmpty { get; }
|
||||
}
|
||||
|
||||
public class DeferredRewardStashService : IDeferredRewardStashService
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
private readonly TbItem _itemTable = GContext.container.Resolve<Tables>().TbItem;
|
||||
private readonly PlayerItemData _playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
private CompositeDisposable _disposables;
|
||||
private List<ItemData> _items;
|
||||
public IReadOnlyList<ItemData> Items => _items;
|
||||
public bool IsEmpty => _items is not { Count: > 0 };
|
||||
private const string Key = "DeferredRewardStashService", Comma = ",", Splitter = "|";
|
||||
|
||||
|
||||
public DeferredRewardStashService()
|
||||
{
|
||||
_disposables = new CompositeDisposable();
|
||||
GContext.OnEvent<EventStashDrop>().Subscribe(StashItemsByDrop).AddTo(_disposables);
|
||||
GContext.OnEvent<EventStashItems>().Subscribe(StashItems).AddTo(_disposables);
|
||||
GContext.OnEvent<EventStashDropList>().Subscribe(StashItemsByDropList).AddTo(_disposables);
|
||||
GContext.OnEvent<EventStashItem>().Subscribe(e => StashItem(e.Item)).AddTo(_disposables);
|
||||
}
|
||||
|
||||
public void DisposeSubscriptions()
|
||||
{
|
||||
_disposables.Dispose();
|
||||
_disposables = null;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_items = new List<ItemData>();
|
||||
//_items.Clear();
|
||||
}
|
||||
|
||||
public void Flush(RewardType rewardType = RewardType.FishBoxFly, bool isShowReward = true)
|
||||
{
|
||||
Debug.Log("[DeferredRewardStashService] Flush.");
|
||||
string textValue = LocalizationMgr.GetText("UI_MiniBattlePass_DivePopupPanel_8");
|
||||
if (IsEmpty)
|
||||
{
|
||||
rewardType = RewardType.FishBoxFlyRestore;
|
||||
textValue = LocalizationMgr.GetText("UI_FishingRewardPanel_101042");
|
||||
LoadData();
|
||||
}
|
||||
if (IsEmpty)
|
||||
return;
|
||||
if (isShowReward)
|
||||
{
|
||||
ShowData showData = new ShowData(_items, rewardType);
|
||||
showData.textValue = textValue;
|
||||
//UI_FishingRewardPanel_101041
|
||||
game.PlayerDataStorage.PlayerSaveAction.Present(() => GContext.Publish(showData));
|
||||
}
|
||||
_playerItemData.AddItem(_items, null);
|
||||
Reset();
|
||||
UploadData();
|
||||
}
|
||||
|
||||
private void StashItemsByDropList(EventStashDropList e)
|
||||
{
|
||||
foreach (var dropId in e.DropList)
|
||||
{
|
||||
var items = _playerItemData.GetItemDataByDropIdLureInflation(e.Inflate, dropId);
|
||||
foreach (var item in items)
|
||||
{
|
||||
item.changeSource = e.changeSource;
|
||||
StashItem(item);
|
||||
}
|
||||
}
|
||||
UploadData();
|
||||
}
|
||||
|
||||
private void StashItemsByDrop(EventStashDrop e)
|
||||
{
|
||||
var items = _playerItemData.GetItemDataByDropIdLureInflation(e.Inflate, e.DropId);
|
||||
foreach (var item in items)
|
||||
{
|
||||
item.changeSource = e.changeSource;
|
||||
StashItem(item);
|
||||
}
|
||||
UploadData();
|
||||
}
|
||||
|
||||
private void StashItems(EventStashItems e)
|
||||
{
|
||||
var items = e.Items;
|
||||
foreach (var item in items)
|
||||
{
|
||||
StashItem(item);
|
||||
}
|
||||
UploadData();
|
||||
}
|
||||
|
||||
private void StashItem(ItemData item)
|
||||
{
|
||||
if (!_itemTable.DataMap.ContainsKey(item.id))
|
||||
{
|
||||
//Debug.LogError($"Item id {item.id} not found in item table.");
|
||||
return;
|
||||
}
|
||||
// Debug.Log($"[RewardStash] Item {item.id} * {item.count} Stashed.");
|
||||
var itemDropPackageItems = GetItemDropPackageItemList(item);
|
||||
if (itemDropPackageItems != null && itemDropPackageItems.Count > 0)
|
||||
{
|
||||
GContext.container.Resolve<PlayerItemData>().LureInflation(itemDropPackageItems, item.inflate);
|
||||
foreach (var dropItem in itemDropPackageItems)
|
||||
{
|
||||
dropItem.changeSource = item.changeSource;
|
||||
_items.Add(dropItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_items.Add(item);
|
||||
}
|
||||
|
||||
UploadData();
|
||||
// _playerItemData.AddItem(item);
|
||||
}
|
||||
List<ItemData> GetItemDropPackageItemList(ItemData itemData)
|
||||
{
|
||||
Item item = _itemTable.GetOrDefault(itemData.id);
|
||||
if (item.Type == 9)
|
||||
{
|
||||
switch (item.SubType)
|
||||
{
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
return _playerItemData.GetItemDropPackageItemList(item.RedirectID, itemData.count);
|
||||
case 7:
|
||||
case 8:
|
||||
return _playerItemData.GetItemDropItemList(item.RedirectID, itemData.count);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var item in _items)
|
||||
sb.Append(item.id + Comma + (int)item.count + Splitter);
|
||||
if (sb.Length > 0)
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private List<ItemData> Deserialize(string s)
|
||||
{
|
||||
if (s == null || s == "")
|
||||
return new List<ItemData>();
|
||||
var items = s.Split(Splitter);
|
||||
var res = new List<ItemData>();
|
||||
foreach (var item in items)
|
||||
{
|
||||
var split = item.Split(Comma);
|
||||
if (split.Length != 2)
|
||||
{
|
||||
Debug.LogWarning($"[DeferredRewardStashService]Wrong amount of parameters in \"{item}\".");
|
||||
continue;
|
||||
}
|
||||
var id = int.Parse(split[0]);
|
||||
var count = int.Parse(split[1]);
|
||||
var itemData = new ItemData(id, count);
|
||||
res.Add(itemData);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void UploadData()
|
||||
{
|
||||
var s = Serialize();
|
||||
saveStore.Write(Key, s).RequireAccepted();
|
||||
}
|
||||
|
||||
public void LoadData()
|
||||
{
|
||||
var s = saveStore.Read(Key).ValueOrThrow();
|
||||
Reset();
|
||||
if (string.IsNullOrEmpty(s))
|
||||
return;
|
||||
_items = Deserialize(s);
|
||||
}
|
||||
|
||||
public class EventStashItems
|
||||
{
|
||||
public List<ItemData> Items;
|
||||
}
|
||||
|
||||
public class EventStashDrop
|
||||
{
|
||||
public int DropId;
|
||||
public float Inflate = 0;
|
||||
public ChangeSource changeSource;
|
||||
}
|
||||
|
||||
public class EventStashDropList
|
||||
{
|
||||
public List<int> DropList;
|
||||
public ChangeSource changeSource;
|
||||
public float Inflate = 0;
|
||||
}
|
||||
|
||||
public class EventStashItem
|
||||
{
|
||||
public ItemData Item;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
public interface IFootPrintService
|
||||
{
|
||||
public EFootPrint FootPrint { get; }
|
||||
public void UpdateFootPrint(EFootPrint newState);
|
||||
public void UploadData();
|
||||
public void LoadData(string data);
|
||||
public bool HasFootPrint(EFootPrint state);
|
||||
}
|
||||
|
||||
// ReSharper disable once ClassNeverInstantiated.Global
|
||||
public class FootPrintService : IFootPrintService
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
private EFootPrint _footPrint;
|
||||
private ulong _kvCompleteFootPrint;
|
||||
internal ulong CompleteFootPrintForKVValidation => _kvCompleteFootPrint;
|
||||
internal static game.PlayerDataStorage.PlayerKVFootPrintState DecodeForKVLoad(
|
||||
System.Collections.Generic.IReadOnlyDictionary<string, string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors)
|
||||
=> game.PlayerDataStorage.PlayerKVSaveSchema.DecodeFootPrint(raw, errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
_kvCompleteFootPrint = 0;
|
||||
_footPrint = 0;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVFootPrintState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
_kvCompleteFootPrint = state.Bits;
|
||||
// This legacy view contains only the low 32 bits. The ulong field retains the complete KV state.
|
||||
_footPrint = unchecked((EFootPrint)(int)(uint)state.Bits);
|
||||
}
|
||||
public EFootPrint FootPrint => _footPrint;
|
||||
public void UpdateFootPrint(EFootPrint newState)
|
||||
{
|
||||
_footPrint |= newState;
|
||||
UploadData();
|
||||
}
|
||||
|
||||
public void UploadData()
|
||||
{
|
||||
_kvCompleteFootPrint |= unchecked((uint)(int)_footPrint);
|
||||
var data = _kvCompleteFootPrint;
|
||||
SaveStore.Write("FootPrint", data.ToString()).RequireAccepted();
|
||||
}
|
||||
|
||||
public void LoadData(string data)
|
||||
{
|
||||
_kvCompleteFootPrint = ulong.Parse(data);
|
||||
_footPrint = unchecked((EFootPrint)(int)(uint)_kvCompleteFootPrint);
|
||||
}
|
||||
|
||||
public bool HasFootPrint(EFootPrint state)
|
||||
{
|
||||
return (_footPrint & state) == state;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Record Status. Max out at 64th record.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum EFootPrint : int
|
||||
{
|
||||
HasCommunityGuidancePanelBeenOpened = 1 << 0,
|
||||
IsFaceBookRewardClaimed = 1 << 1,
|
||||
IsInstagramRewardClaimed = 1 << 2,
|
||||
IsDiscordRewardClaimed = 1 << 3,
|
||||
IsTiktokRewardClaimed = 1 << 4,
|
||||
IsYouTubeRewardClaimed = 1 << 5,
|
||||
IsEventCanGuidePoped = 1 << 6,
|
||||
IsFaceBookRewardLink = 1 << 7,
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
using game.PlayerDataStorage;
|
||||
using asap.core;
|
||||
using asap.playfab.async;
|
||||
using GameCore;
|
||||
using PlayFab.ClientModels;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class FriendService
|
||||
{
|
||||
[Inject]
|
||||
public IUserService userService { get; set; }
|
||||
|
||||
[Inject]
|
||||
public ICustomServerMgr customServerMgr { get; set; }
|
||||
|
||||
[Inject]
|
||||
public IRTService rts { get; set; }
|
||||
|
||||
[Inject]
|
||||
public cfg.Tables tables { get; set; }
|
||||
|
||||
[Inject] public IPlayerSaveStore SaveStore { get; set; }
|
||||
public bool IsCurrent => SaveStore.IsCurrent && !userService.LoginLifetime.IsCancellationRequested;
|
||||
bool linkRewardPending;
|
||||
|
||||
public const string InviteLinkRewardCountKey = "InviteLinkRewardCount";
|
||||
IDisposable linkCompletedDisposable;
|
||||
|
||||
public void GetFriendData()
|
||||
{
|
||||
//GetFriendLeaderboard();
|
||||
GetFriendList();
|
||||
//GetInvitedListCount();
|
||||
}
|
||||
|
||||
#region 邀请Link相关
|
||||
public int InviteLinkNumber { private set; get; }
|
||||
int InviteLinkRewardCount = 0;
|
||||
|
||||
|
||||
void SetLinkAwardData()
|
||||
{
|
||||
List<cfg.Invite> invites = tables.TbInvite.DataList;
|
||||
//已经领取奖励的数量
|
||||
var read = SaveStore.Read(InviteLinkRewardCountKey);
|
||||
var value = read.ValueOrThrow();
|
||||
if (read.Found == false) InviteLinkRewardCount = 0;
|
||||
else if (!int.TryParse(value, out InviteLinkRewardCount) || InviteLinkRewardCount < 0 || InviteLinkRewardCount > invites.Count)
|
||||
throw new InvalidOperationException("Invalid invitation reward count.");
|
||||
int canRewardCount = InviteLinkRewardCount;
|
||||
for (int i = InviteLinkRewardCount; i < invites.Count; i++)
|
||||
{
|
||||
//领取奖励
|
||||
if (invites[i].NumInvite <= InviteLinkNumber)
|
||||
{
|
||||
canRewardCount = i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Invite, canRewardCount > InviteLinkRewardCount);
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("invite_increase"))
|
||||
{
|
||||
e.AddContent("inviteprogress", InviteLinkNumber)
|
||||
.AddContent("rewardprogress", InviteLinkRewardCount);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public async Task<int> OnReceiveLinkAward()
|
||||
{
|
||||
if (!IsCurrent) throw new OperationCanceledException();
|
||||
if (linkRewardPending) throw new InvalidOperationException("Invitation reward is pending or requires recovery.");
|
||||
SetLinkAwardData();
|
||||
int canRewardCount = InviteLinkRewardCount;
|
||||
List<cfg.Invite> invites = tables.TbInvite.DataList;
|
||||
List<int> DropIDs = new List<int>();
|
||||
for (int i = InviteLinkRewardCount; i < invites.Count; i++)
|
||||
{
|
||||
//领取奖励
|
||||
if (invites[i].NumInvite <= InviteLinkNumber)
|
||||
{
|
||||
canRewardCount = i + 1;
|
||||
DropIDs.Add(invites[i].DropID);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (InviteLinkRewardCount != canRewardCount)
|
||||
{
|
||||
linkRewardPending = true;
|
||||
var items = GContext.container.Resolve<PlayerItemData>();
|
||||
var player = GContext.container.Resolve<PlayerData>();
|
||||
await PlayerSaveAction.RunAsync(SaveStore, "invite-reward", () =>
|
||||
{
|
||||
items.AddItemByDropList(DropIDs, null);
|
||||
PlayerSaveAction.Present(() => GContext.Publish(new ShowData()));
|
||||
InviteLinkRewardCount = canRewardCount;
|
||||
SaveStore.Write(InviteLinkRewardCountKey, InviteLinkRewardCount.ToString()).RequireAccepted();
|
||||
}, () => player.SavePlayerData(), userService.LoginLifetime);
|
||||
if (!IsCurrent) throw new OperationCanceledException();
|
||||
linkRewardPending = false;
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Invite, false);
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("invite_reward"))
|
||||
{
|
||||
e.AddContent("inviteprogress", InviteLinkNumber)
|
||||
.AddContent("rewardprogress", InviteLinkRewardCount);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return InviteLinkRewardCount;
|
||||
}
|
||||
#endregion 邀请Link相关
|
||||
|
||||
#region 判断是否好友
|
||||
public bool IsFriend(string playFabID)
|
||||
{
|
||||
if (m_FriendList == null || m_FriendList.Count < 1 || string.IsNullOrEmpty(playFabID)) return false;
|
||||
|
||||
foreach (var friend in m_FriendList)
|
||||
{
|
||||
if (friend.playFabId.Equals(playFabID))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 获取好友列表
|
||||
private List<LeadboardItemData> m_FriendList = new List<LeadboardItemData>();
|
||||
public List<LeadboardItemData> FriendList { get { return m_FriendList; } }
|
||||
public bool IsFriendListRefreshing { get; private set; } = true;
|
||||
public async void GetFriendList()
|
||||
{
|
||||
GetFriendsListRequest getFriendsListRequest = new GetFriendsListRequest
|
||||
{
|
||||
ProfileConstraints = new PlayerProfileViewConstraints()
|
||||
{
|
||||
ShowAvatarUrl = true,
|
||||
ShowDisplayName = true,
|
||||
ShowStatistics = true,
|
||||
ShowLastLogin = true,
|
||||
}
|
||||
};
|
||||
|
||||
var result = await PlayFabClientAsyncAPI.GetFriendsListAsync(getFriendsListRequest);
|
||||
var list = result?.Friends;
|
||||
if (list != null)
|
||||
{
|
||||
string userId = GContext.container.Resolve<IUserService>().UserId;
|
||||
List<LeadboardItemData> leadboardList = new List<LeadboardItemData>();
|
||||
int count = list.Count;
|
||||
foreach (FriendInfo friendInfo in list)
|
||||
{
|
||||
if (friendInfo.Profile == null) continue;
|
||||
var listStatistic = friendInfo.Profile.Statistics;
|
||||
if (listStatistic == null) continue;
|
||||
|
||||
if (userId.Equals(friendInfo.FriendPlayFabId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(friendInfo.Profile.DisplayName))
|
||||
{
|
||||
friendInfo.Profile.DisplayName = userService.GetDefaultName(friendInfo.FriendPlayFabId);
|
||||
}
|
||||
|
||||
int level = 0;
|
||||
foreach (var item in listStatistic)
|
||||
{
|
||||
if (item == null) continue;
|
||||
if (item.Name == LeadboardData.leadboardLevelName)
|
||||
{
|
||||
//level = item.Value / LeadboardData.LV_MODELING;
|
||||
level = item.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
userService.SetPlayInfo(friendInfo.FriendPlayFabId, friendInfo.Profile.DisplayName, friendInfo.Profile.AvatarUrl);
|
||||
|
||||
LeadboardItemData friendData = new LeadboardItemData();
|
||||
friendData.playFabId = friendInfo.FriendPlayFabId;
|
||||
friendData.displayName = friendInfo.Profile.DisplayName;
|
||||
friendData.avatarUrl = friendInfo.Profile.AvatarUrl;
|
||||
friendData.LastLogin = friendInfo.Profile.LastLogin;
|
||||
friendData.value = level;
|
||||
friendData.position = 0;
|
||||
friendData.rank = 0;
|
||||
|
||||
leadboardList.Add(friendData);
|
||||
}
|
||||
|
||||
leadboardList.Sort((item1, item2) =>
|
||||
{
|
||||
return item2.value.CompareTo(item1.value);
|
||||
});
|
||||
|
||||
count = leadboardList.Count;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
LeadboardItemData itemData = leadboardList[i];
|
||||
itemData.position = i;
|
||||
itemData.rank = i + 1;
|
||||
}
|
||||
|
||||
m_FriendList = leadboardList;
|
||||
IsFriendListRefreshing = false;
|
||||
|
||||
GContext.Publish<SocialFriendListChangedData>(new SocialFriendListChangedData());
|
||||
#if AGG
|
||||
GEvent.SetProp("friends_count", count);
|
||||
GEvent.SetUserProp("friends_count", count);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 邀请好友
|
||||
public async Task<string> AddFriend(string playfabID)
|
||||
{
|
||||
int count = m_FriendList.Count;
|
||||
AddFriendReq getFriendListReq = new AddFriendReq()
|
||||
{
|
||||
MySelf = new BFriendInfo
|
||||
{
|
||||
PlayFabId = userService.UserId,
|
||||
AvatarUrl = userService.AvatarUrl,
|
||||
DisplayName = userService.DisplayName,
|
||||
Level = GContext.container.Resolve<PlayerData>().lv
|
||||
},
|
||||
MyFriendCount = count,
|
||||
FriendPlayFabId = playfabID,
|
||||
};
|
||||
|
||||
FriendHelper.FixBFriendInfoData(getFriendListReq.MySelf);
|
||||
|
||||
return await customServerMgr.FriendRequest("AddFriend", getFriendListReq);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 删除好友
|
||||
public async Task<bool> DeleteFriend(string friendPlayfabID)
|
||||
{
|
||||
var req = new FriendDeleteReq()
|
||||
{
|
||||
FriendPlayFabId = friendPlayfabID,
|
||||
};
|
||||
var json = await customServerMgr.FriendRequest("DeleteFriend", req);
|
||||
if (string.IsNullOrEmpty(json)) return false;
|
||||
var resp = Newtonsoft.Json.JsonConvert.DeserializeObject<FriendDeleteResp>(json);
|
||||
if (resp.Code == FriendHelper.Code_OK)
|
||||
{
|
||||
m_FriendList.RemoveAll(f => f.playFabId == friendPlayfabID);
|
||||
GContext.Publish<SocialFriendListChangedData>(new SocialFriendListChangedData());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 搜索好友
|
||||
public async Task<string> SearchFriend(string playfabID)
|
||||
{
|
||||
SearchFriendReq searchFriendReq = new SearchFriendReq()
|
||||
{
|
||||
PlayFabId = playfabID
|
||||
};
|
||||
|
||||
return await customServerMgr.FriendRequest("SearchFriend", searchFriendReq);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 接受好友邀请/忽略好友邀请
|
||||
// status:1:同意邀请;2:忽略, playfabID 接受/忽略 friendPlayFabID 的邀请
|
||||
public async Task<string> AcceptFriend(int status, string playfabID, string friendPlayFabID)
|
||||
{
|
||||
AcceptFriendReq acceptFriendReq = new AcceptFriendReq()
|
||||
{
|
||||
Status = status,
|
||||
PlayFabId = playfabID,
|
||||
FriendPlayFabId = friendPlayFabID
|
||||
};
|
||||
|
||||
return await customServerMgr.FriendRequest("AcceptFriend", acceptFriendReq);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 好友主页
|
||||
public async Task<string> GetHomePage(string playfabID, int pageNo)
|
||||
{
|
||||
GetHomePageReq getHomePageReq = new GetHomePageReq()
|
||||
{
|
||||
PlayFabId = playfabID,
|
||||
PageNo = pageNo
|
||||
};
|
||||
|
||||
return await customServerMgr.FriendRequest("GetHomePage", getHomePageReq);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 好友邀请列表&推荐列表
|
||||
public async Task<string> GetRecommendedFriends(string playfabID, int pageNo)
|
||||
{
|
||||
GetRecommendedFriendsReq acceptFriendReq = new GetRecommendedFriendsReq()
|
||||
{
|
||||
PlayFabId = playfabID,
|
||||
PageNo = pageNo
|
||||
};
|
||||
|
||||
return await customServerMgr.FriendRequest("GetRecommendedFriends", acceptFriendReq);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 获取申请成为我的朋友的列表
|
||||
public async Task<string> GetInvitedList()
|
||||
{
|
||||
return await customServerMgr.FriendRequest("GetInvitedList", "");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 获取邀请自己的列表数量
|
||||
public async void GetInvitedListCount()
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
string json = await customServerMgr.FriendRequest("GetInvitedListCount", "");
|
||||
if (!IsCurrent) return;
|
||||
if (!string.IsNullOrEmpty(json))
|
||||
{
|
||||
GetInvitedListCountResp getRecommendedFriendsResp = Newtonsoft.Json.JsonConvert.DeserializeObject<GetInvitedListCountResp>(json);
|
||||
if (getRecommendedFriendsResp != null)
|
||||
{
|
||||
if (getRecommendedFriendsResp.FriendInvitedCount >= 0)
|
||||
{
|
||||
await Awaiters.NextFrame;
|
||||
if (!IsCurrent) return;
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Friend_Add, getRecommendedFriendsResp.FriendInvitedCount > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[FriendService::GetInvitedListCount] FriendInvitedCount Failed:");
|
||||
}
|
||||
|
||||
if (getRecommendedFriendsResp.DeepLinkInvitedCount > 0)
|
||||
{
|
||||
InviteLinkNumber = getRecommendedFriendsResp.DeepLinkInvitedCount;
|
||||
SetLinkAwardData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 邀请码处理
|
||||
const string chars = "0M4KL3FZSTG8XCY5E6VW179A2UBDHQRIJNOP";
|
||||
readonly ulong baseLength = (ulong)chars.Length;
|
||||
public string EncryptUlongToInvitationCode(ulong number)
|
||||
{
|
||||
StringBuilder encoded = new StringBuilder();
|
||||
while (number > 0)
|
||||
{
|
||||
encoded.Insert(0, chars[(int)(number % baseLength)]);
|
||||
number /= baseLength;
|
||||
}
|
||||
return encoded.ToString().PadLeft(14, chars[0]); // Pad with '0' if necessary
|
||||
}
|
||||
public ulong DecryptInvitationCodeToUlong(string invitationCode)
|
||||
{
|
||||
ulong number = 0;
|
||||
foreach (char c in invitationCode)
|
||||
{
|
||||
number *= baseLength;
|
||||
number += (ulong)chars.IndexOf(c);
|
||||
}
|
||||
return number;
|
||||
}
|
||||
public string GetInvitationCodeByPlayFabID(string playFabID)
|
||||
{
|
||||
ulong code = Convert.ToUInt64(playFabID, 16);
|
||||
string invitationCode = EncryptUlongToInvitationCode(code);
|
||||
|
||||
return InsertFormat(invitationCode, 3, "-");
|
||||
}
|
||||
public string InsertFormat(string input, int interval, string value)
|
||||
{
|
||||
for (int i = interval; i < input.Length; i += interval + 1)
|
||||
input = input.Insert(i, value);
|
||||
return input;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 获得X天内登录过的活跃好友数量
|
||||
|
||||
public int GetActiveFriendCount(int xDays)
|
||||
{
|
||||
return FriendList.Count(data =>
|
||||
data.LastLogin is not null &&
|
||||
ZZTimeHelper.UtcNow() - data.LastLogin <= TimeSpan.FromDays(xDays));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 判断当前用户是否处于被邀请状态
|
||||
public async Task<string> IsFriendWaitingForAccept(string playfabID, string friendPlayfabID)
|
||||
{
|
||||
IsFriendWaitingForAcceptReq req = new IsFriendWaitingForAcceptReq()
|
||||
{
|
||||
PlayFabId = playfabID,
|
||||
FriendPlayFabId = friendPlayfabID
|
||||
};
|
||||
|
||||
return await customServerMgr.FriendRequest("IsFriendWaitingForAccept", req);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 后台操作
|
||||
List<FriendAddPanelBackendOperation> m_FriendAddPanelBackendOperationList = new List<FriendAddPanelBackendOperation>();
|
||||
public void StartInvite(string playfabID)
|
||||
{
|
||||
var backendOperation = new FriendAddPanelBackendOperation();
|
||||
m_FriendAddPanelBackendOperationList.Add(backendOperation);
|
||||
backendOperation.StartInvite(playfabID, OnInviteCallback);
|
||||
}
|
||||
public void OnInviteCallback(FriendAddPanelBackendOperation backendOperation, string playfabID, int code)
|
||||
{
|
||||
if (backendOperation != null)
|
||||
{
|
||||
backendOperation.Clear();
|
||||
m_FriendAddPanelBackendOperationList.Remove(backendOperation);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void Release()
|
||||
{
|
||||
linkCompletedDisposable?.Dispose();
|
||||
linkCompletedDisposable = null;
|
||||
|
||||
m_FriendAddPanelBackendOperationList.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
using game.PlayerDataStorage;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using game;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using UniRx;
|
||||
|
||||
public class FetchMailRequest
|
||||
{
|
||||
public DateTime? lastSyncTime;
|
||||
}
|
||||
|
||||
public class FetchMailResp
|
||||
{
|
||||
public bool success;
|
||||
public List<GameMail> mails;
|
||||
public string error;
|
||||
}
|
||||
|
||||
public class MailStateInfo
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public EMailState state { get; set; }
|
||||
|
||||
public MailStateInfo(GameMail mail, EMailState newState)
|
||||
{
|
||||
Id = mail.Id.ToString();
|
||||
state = mail.State | newState;
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateMailStateRequest
|
||||
{
|
||||
public MailStateInfo[] mailStates;
|
||||
}
|
||||
|
||||
public class UpdateMailStateResp
|
||||
{
|
||||
public bool success;
|
||||
public MailStateInfo[] states;
|
||||
}
|
||||
|
||||
public class FetchMailEvt
|
||||
{
|
||||
DateTime TimeStamp;
|
||||
}
|
||||
|
||||
public class MailInfo
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public EMailType Type { get; set; }
|
||||
|
||||
public int expireDays { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public string From { get; set; }
|
||||
|
||||
public string Content { get; set; }
|
||||
|
||||
public int[] DropIds { get; set; }
|
||||
}
|
||||
|
||||
public class SendMailRequest
|
||||
{
|
||||
public MailInfo[] mailInfos;
|
||||
}
|
||||
|
||||
public class SendMailResp
|
||||
{
|
||||
public bool success;
|
||||
public List<GameMail> mails;
|
||||
public string error;
|
||||
}
|
||||
|
||||
public class AutoMailData
|
||||
{
|
||||
public string Id;
|
||||
public int levelMin;
|
||||
public int levelMax;
|
||||
public DateTime accountCreateTime;
|
||||
public DateTime loginTimeMin;
|
||||
public DateTime loginTimeMax;
|
||||
public int expireDays;
|
||||
public int DropID;
|
||||
|
||||
public AutoMailData(cfg.AutoDeliveryMail mail)
|
||||
{
|
||||
Id = mail.MailID.ToString();
|
||||
levelMin = mail.AccountLevelRange[0];
|
||||
levelMax = mail.AccountLevelRange[1] == -1 ? int.MaxValue : mail.AccountLevelRange[1];
|
||||
accountCreateTime = DateTime.Parse(mail.AccountCreateTimestamp);
|
||||
loginTimeMin = DateTime.Parse(mail.LoginTimestampRange[0]);
|
||||
loginTimeMax = DateTime.Parse(mail.LoginTimestampRange[1]);
|
||||
DropID = mail.DropID;
|
||||
expireDays = mail.ExpireDay;
|
||||
}
|
||||
|
||||
public bool IsSatisfy(DateTime loginTime, DateTime CreateTime, int level)
|
||||
{
|
||||
return loginTime >= loginTimeMin && loginTime <= loginTimeMax
|
||||
&& CreateTime < accountCreateTime
|
||||
&& level >= levelMin && level <= levelMax;
|
||||
}
|
||||
|
||||
public MailInfo ToMailInfo()
|
||||
{
|
||||
return new MailInfo
|
||||
{
|
||||
Id = Id.ToString(),
|
||||
Type = EMailType.AutoDelivery,
|
||||
DropIds = new[] { DropID },
|
||||
expireDays = expireDays
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class MailUrls
|
||||
{
|
||||
public const string Fetch = "mail/fetch";
|
||||
public const string UpdateState = "mail/updateState";
|
||||
public const string Send = "mail/send";
|
||||
}
|
||||
|
||||
public class InGameMailService : IInGameMailService
|
||||
{
|
||||
private MailCache _mailCache;
|
||||
private BaseMailHandler _handler;
|
||||
readonly IUserService userService;
|
||||
private List<AutoMailData> _autoMailDatas;
|
||||
|
||||
private CompositeDisposable _disposables;
|
||||
|
||||
private bool _forceFetch = false;
|
||||
|
||||
//private const int Fetch_Interval = 3;
|
||||
|
||||
public bool IsUnlock { get; set; }
|
||||
|
||||
|
||||
public InGameMailService(ICustomServerMgr customServerMgr, cfg.Tables tables, IUserService userService, IPlayerSaveStore saveStore)
|
||||
{
|
||||
this._mailCache = new MailCache(tables.TbAutoDeliveryMail);
|
||||
this.userService = userService;
|
||||
this._handler = new BaseMailHandler(_mailCache, customServerMgr, userService, saveStore);
|
||||
_autoMailDatas = tables.TbAutoDeliveryMail.DataList
|
||||
.Select(x => new AutoMailData(x)).ToList();
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
if (!userService.IsAuthorized) throw new InvalidOperationException("Mail cache requires an authenticated user.");
|
||||
_mailCache.Load(userService.UserId);
|
||||
|
||||
_disposables = new CompositeDisposable();
|
||||
|
||||
var minActiveLevel = _autoMailDatas.Min(x => x.levelMin) - 1;
|
||||
|
||||
_ = _handler.FetchMail();
|
||||
|
||||
GContext.OnEvent<OnLvChangeEvent>()
|
||||
.Skip(1)
|
||||
.Where(x => x.lvl >= minActiveLevel)
|
||||
.Subscribe(OnLevelChange)
|
||||
.AddTo(_disposables);
|
||||
|
||||
//GContext.OnEvent<RestartShowHomeUIEvent>()
|
||||
//.Skip(1)
|
||||
//.Subscribe(OnHomeUIShow)
|
||||
//.AddTo(_disposables);
|
||||
|
||||
var rtService = GContext.container.Resolve<IRTService>();
|
||||
|
||||
rtService.OnEvent<FetchMailEvt>(async evt => await _handler.FetchMail())
|
||||
.AddTo(_disposables);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_handler.Close();
|
||||
_mailCache?.Unload();
|
||||
_disposables?.Dispose();
|
||||
_disposables = null;
|
||||
}
|
||||
|
||||
public async Task FetchMail()
|
||||
{
|
||||
await _handler.FetchMail();
|
||||
}
|
||||
|
||||
public IList<GameMail> GetAllMail()
|
||||
{
|
||||
return _mailCache.GetAll();
|
||||
}
|
||||
|
||||
public async Task<bool> MarkRead(GameMail mail)
|
||||
{
|
||||
return await _handler.MarkAsRead(mail);
|
||||
}
|
||||
|
||||
public async Task<bool> MarkRead(GameMail[] mails = null)
|
||||
{
|
||||
return await _handler.MarkAsRead(mails);
|
||||
}
|
||||
|
||||
public async Task<bool> DelMail(GameMail mail)
|
||||
{
|
||||
return await _handler.DeleteMail(mail);
|
||||
}
|
||||
|
||||
public async Task<bool> DelMail(GameMail[] mails = null)
|
||||
{
|
||||
return await _handler.DeleteMail(mails);
|
||||
}
|
||||
|
||||
public async Task<bool> TakeReward(GameMail mail)
|
||||
{
|
||||
return await _handler.TakeReward(mail);
|
||||
}
|
||||
|
||||
public async Task<bool> TakeReward(GameMail[] mails)
|
||||
{
|
||||
return await _handler.TakeReward(mails);
|
||||
}
|
||||
|
||||
private void OnLevelChange(OnLvChangeEvent evt)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEngine.Debug.Log("[InGameMailService] OnLevelChange");
|
||||
#endif
|
||||
GenAutoDeliveryMail();
|
||||
}
|
||||
|
||||
//private async void OnHomeUIShow(RestartShowHomeUIEvent evt)
|
||||
//{
|
||||
|
||||
//#if UNITY_EDITOR
|
||||
//var deltaTime = _mailCache.LastSyncTime == null ? 0 : (ZZTimeHelper.UtcNow() - _mailCache.LastSyncTime.Value).TotalSeconds;
|
||||
//UnityEngine.Debug.Log($"[InGameMailService] OnHomeUIShow deltaTime {deltaTime}");
|
||||
//#endif
|
||||
//if(_mailCache.LastSyncTime == null
|
||||
//|| _forceFetch
|
||||
//|| ZZTimeHelper.UtcNow() - _mailCache.LastSyncTime.Value > TimeSpan.FromMinutes(Fetch_Interval))
|
||||
//{
|
||||
//if(await _handler.FetchMail())
|
||||
//{
|
||||
//_forceFetch = false;
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
|
||||
private async void GenAutoDeliveryMail()
|
||||
{
|
||||
var playerData = GContext.container.Resolve<PlayerData>();
|
||||
var userData = userService;
|
||||
if (!_handler.IsCurrent) return;
|
||||
|
||||
var mailsToSend = _autoMailDatas.Where(m => !_mailCache.HasMail(m.Id)
|
||||
&& m.IsSatisfy(userData.LoginTime, userData.CreateTime, playerData.lv))
|
||||
.Select(m => m.ToMailInfo())
|
||||
.ToArray();
|
||||
|
||||
if (mailsToSend.Length > 0)
|
||||
{
|
||||
if (await _handler.SendMail(mailsToSend) && _handler.IsCurrent)
|
||||
{
|
||||
_forceFetch = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
var mailids = mailsToSend.Select(m => m.Id);
|
||||
var mailidsStr = string.Join(',', mailids);
|
||||
UnityEngine.Debug.Log($"[InGameMailService] GenAutoDeliveryMail {mailidsStr}");
|
||||
#endif
|
||||
}
|
||||
|
||||
public void PrintCacheMail()
|
||||
{
|
||||
UnityEngine.Debug.Log(_mailCache.PrintCacheMails());
|
||||
}
|
||||
}
|
||||
|
||||
public class BaseMailHandler : IMailHandler
|
||||
{
|
||||
protected MailCache _cache;
|
||||
protected ICustomServerMgr _customServerMgr;
|
||||
readonly IUserService userService;
|
||||
readonly IPlayerSaveStore saveStore;
|
||||
bool closed, rewardPending;
|
||||
public bool IsCurrent => !closed && saveStore.IsCurrent && !userService.LoginLifetime.IsCancellationRequested;
|
||||
public void Close() { closed = true; }
|
||||
|
||||
public BaseMailHandler(MailCache cache, ICustomServerMgr customServerMgr, IUserService userService, IPlayerSaveStore saveStore)
|
||||
{
|
||||
this.userService = userService;
|
||||
this.saveStore = saveStore;
|
||||
this._cache = cache;
|
||||
this._customServerMgr = customServerMgr;
|
||||
}
|
||||
|
||||
public virtual async Task<bool> FetchMail()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsCurrent) return false;
|
||||
await Awaiters.NextFrame;
|
||||
if (!IsCurrent) return false;
|
||||
#if UNITY_EDITOR
|
||||
UnityEngine.Debug.Log($"[InGameMailService] Start Fetch Mails");
|
||||
#endif
|
||||
var req = new FetchMailRequest()
|
||||
{
|
||||
lastSyncTime = _cache.LastSyncTime
|
||||
};
|
||||
|
||||
if (!IsCurrent) return false;
|
||||
var result = await _customServerMgr.CustomServerPost(MailUrls.Fetch, JsonConvert.SerializeObject(req));
|
||||
if (!IsCurrent) return false;
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
var resp = JsonConvert.DeserializeObject<FetchMailResp>(result);
|
||||
if (resp.success)
|
||||
{
|
||||
if (resp.mails != null && resp.mails.Count > 0)
|
||||
{
|
||||
_cache.Cache(resp.mails);
|
||||
_cache.Save();
|
||||
var UnreadMailCount = _cache.UnreadMailCount;
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Unread_Mail, UnreadMailCount > 0, UnreadMailCount);
|
||||
#if UNITY_EDITOR
|
||||
UnityEngine.Debug.Log($"[InGameMailService] FetchMail {UnreadMailCount} LastSyncTime = {_cache.LastSyncTime}");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.UpdateSyncTime();
|
||||
#if UNITY_EDITOR
|
||||
UnityEngine.Debug.Log($"[InGameMailService] No Mails Fetch Fetched LastSyncTime = {_cache.LastSyncTime}");
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[InGameMailService] FetchMail Failed {resp.error} ");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogException(e);
|
||||
}
|
||||
|
||||
UnityEngine.Debug.LogWarning("[InGameMailService] FetchMail Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> SendMail(MailInfo[] mailInfos)
|
||||
{
|
||||
try
|
||||
{
|
||||
var req = new SendMailRequest()
|
||||
{
|
||||
mailInfos = mailInfos
|
||||
};
|
||||
|
||||
if (!IsCurrent) return false;
|
||||
var result = await _customServerMgr.CustomServerPost(MailUrls.Send, JsonConvert.SerializeObject(req));
|
||||
if (!IsCurrent) return false;
|
||||
if (result != null)
|
||||
{
|
||||
var resp = JsonConvert.DeserializeObject<SendMailResp>(result);
|
||||
if (!resp.success)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[InGameMailService] SendMail Failed {resp.error} ");
|
||||
}
|
||||
return resp.success;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual async Task<bool> UpdateMailState(GameMail[] mails, EMailState state)
|
||||
{
|
||||
var req = new UpdateMailStateRequest()
|
||||
{
|
||||
mailStates = mails.Select(m => new MailStateInfo(m, state)).ToArray()
|
||||
};
|
||||
|
||||
if (!IsCurrent) return false;
|
||||
var result = await _customServerMgr.CustomServerPost(MailUrls.UpdateState, JsonConvert.SerializeObject(req));
|
||||
if (!IsCurrent) return false;
|
||||
if (result != null)
|
||||
{
|
||||
var resp = JsonConvert.DeserializeObject<UpdateMailStateResp>(result);
|
||||
if (resp.success)
|
||||
{
|
||||
foreach (var s in req.mailStates)
|
||||
{
|
||||
var mail = _cache.GetMail(s.Id);
|
||||
if (mail != null) mail.State = s.state;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<bool> MarkAsRead(GameMail mail)
|
||||
{
|
||||
if (!IsCurrent || mail.IsRead) return false;
|
||||
|
||||
var result = await UpdateMailState(new[] { mail }, EMailState.HasRead);
|
||||
if (result) _cache.Save();
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual async Task<bool> MarkAsRead(GameMail[] mails = null)
|
||||
{
|
||||
if (!IsCurrent) return false;
|
||||
if (mails == null)
|
||||
{
|
||||
mails = _cache.GetAll()
|
||||
.Where(m => !m.IsRead)
|
||||
.ToArray();
|
||||
}
|
||||
return await UpdateMailState(mails, EMailState.HasRead);
|
||||
}
|
||||
|
||||
public Task<bool> TakeReward(GameMail mail) => TakeReward(new[] { mail });
|
||||
|
||||
public async Task<bool> TakeReward(GameMail[] mails = null)
|
||||
{
|
||||
if (!IsCurrent || rewardPending) return false;
|
||||
if (saveStore is KVPlayerSaveStore && !saveStore.Read("NewPlayerDataDic").Success) return false;
|
||||
var eligible = (mails ?? _cache.GetAll().ToArray())
|
||||
.Where(m => m != null && m.HasDrops && !m.HasReward)
|
||||
.GroupBy(m => m.Id).Select(g => g.First()).ToArray();
|
||||
if (eligible.Length == 0) return false;
|
||||
// Freeze eligibility and the personal-mail inflation rule before changing server state.
|
||||
var rewards = eligible.Select(m => new { m.Type, Drops = m.DropIds.ToList() }).ToArray();
|
||||
var items = GContext.container.Resolve<PlayerItemData>();
|
||||
var player = GContext.container.Resolve<PlayerData>();
|
||||
rewardPending = true;
|
||||
try
|
||||
{
|
||||
if (!await UpdateMailState(eligible, EMailState.RewardTaken) || !IsCurrent) return false;
|
||||
_cache.Save();
|
||||
await PlayerSaveAction.RunAsync(saveStore, "mail-reward", () =>
|
||||
{
|
||||
foreach (var reward in rewards)
|
||||
{
|
||||
if (reward.Type == EMailType.Personal)
|
||||
items.AddItemByDropListLureInflation(null, reward.Drops, null);
|
||||
else items.AddItemByDropList(reward.Drops, null);
|
||||
}
|
||||
PlayerSaveAction.Present(() => GContext.Publish(new ShowData()));
|
||||
}, () => player.SavePlayerData(), userService.LoginLifetime);
|
||||
if (!IsCurrent) return false;
|
||||
rewardPending = false;
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogException(e);
|
||||
return false;
|
||||
}
|
||||
// On failure keep the claim blocked in this login. Server state and KV saves are not atomic.
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteMail(GameMail mail)
|
||||
{
|
||||
if (!IsCurrent || mail.IsDeleted)
|
||||
return false;
|
||||
|
||||
var result = await UpdateMailState(new[] { mail }, EMailState.HasDeleted);
|
||||
if (result) _cache.Save();
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteMail(GameMail[] mails = null)
|
||||
{
|
||||
if (!IsCurrent) return false;
|
||||
if (mails == null)
|
||||
{
|
||||
mails = _cache.GetAll()
|
||||
.Where(m => m.IsRead && m.HasReward)
|
||||
.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
mails = mails.Where(m => !m.IsDeleted)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
if (mails.Length == 0) return false;
|
||||
|
||||
var result = await UpdateMailState(mails, EMailState.HasDeleted);
|
||||
if (result) _cache.Save();
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using game.PlayerDataStorage;
|
||||
using GameCore;
|
||||
using tysdk;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
// One service belongs to one login lifetime. A revoked instance is never rebound.
|
||||
public sealed class KVUserService : IUserService, IKVUserLoadReceiver, IKVUserLifecycle
|
||||
{
|
||||
[Inject] public ICustomServerMgr customServerMgr { get; set; }
|
||||
[Inject] public IPlayerSaveStore SaveStore { get; set; }
|
||||
readonly PlayerKVRuntimeSelection selection;
|
||||
readonly Func<CookieContainer, HttpMessageHandler> handlerFactory;
|
||||
readonly CancellationTokenSource lifetime = new CancellationTokenSource();
|
||||
readonly Dictionary<string, PlayInfo> profiles = new Dictionary<string, PlayInfo>();
|
||||
readonly Dictionary<string, int> levels = new Dictionary<string, int>();
|
||||
PlayerKVGameLoadCoordinator coordinator;
|
||||
PlayerKVGameSessionHandle handle;
|
||||
PlayerKVInitializationIntents intents;
|
||||
bool attempted, loading, closing, published, promptsBound;
|
||||
Task closeTask;
|
||||
long epoch;
|
||||
string playerId;
|
||||
uint internalId;
|
||||
public string CustomId { get; set; }
|
||||
public string UserId => IsAuthorized ? playerId : null;
|
||||
public string ApiPlayerId => UserId;
|
||||
public uint InternalId => IsAuthorized ? internalId : 0;
|
||||
public bool IsLogin => IsAuthorized;
|
||||
public bool IsAuthorized => published && KVHandoffReady;
|
||||
public CancellationToken LoginLifetime => lifetime.Token;
|
||||
public DateTime LastLoginTime { get; private set; }
|
||||
public DateTime LoginTime { get; private set; }
|
||||
public DateTime CreateTime { get; private set; }
|
||||
public string DisplayName { get; private set; }
|
||||
public string AvatarUrl { get; private set; }
|
||||
public string Language { get; private set; }
|
||||
public string ContinentCode { get; private set; }
|
||||
public Dictionary<string, string> EAccoutUrl { get; private set; } = new Dictionary<string, string>();
|
||||
internal int KVTapJoyBalance { get; private set; }
|
||||
internal PlayerKVGameLoadError KVLastError { get; private set; }
|
||||
internal bool KVHandoffReady => !closing && handle != null && coordinator != null &&
|
||||
coordinator.TryGetCurrentGameHandle(epoch, out var current) && ReferenceEquals(current, handle);
|
||||
bool IKVUserLifecycle.KVHandoffReady => KVHandoffReady;
|
||||
PlayerKVGameLoadError IKVUserLifecycle.KVLastError => KVLastError;
|
||||
|
||||
public KVUserService() : this(SubBoostrap.RuntimeSelection, jar => new HttpClientHandler
|
||||
{ CookieContainer = jar, UseCookies = true, AllowAutoRedirect = false }) { }
|
||||
|
||||
internal KVUserService(PlayerKVRuntimeSelection selection, Func<CookieContainer, HttpMessageHandler> handlerFactory)
|
||||
{
|
||||
this.selection = selection;
|
||||
this.handlerFactory = handlerFactory ?? throw new ArgumentNullException(nameof(handlerFactory));
|
||||
Application.quitting += Logout;
|
||||
}
|
||||
|
||||
public async Task<ELoginStep> Login(Action<int> progress, bool tyrLoginByToken = true)
|
||||
{
|
||||
if (attempted || closing || selection == null || !selection.IsKV ||
|
||||
!selection.TryAdmitPlayer(CustomId, out var canonicalId))
|
||||
{ KVLastError = PlayerKVGameLoadError.Input; return ELoginStep.Api; }
|
||||
// Set before the first await: duplicate calls cannot authenticate or subscribe twice.
|
||||
attempted = true;
|
||||
var requestedId = CustomId;
|
||||
try
|
||||
{
|
||||
if (customServerMgr is not CustomServerMgr server ||
|
||||
server.ApiBaseUri.AbsoluteUri != PlayerKVRuntimeSelection.FormalApiUrl)
|
||||
throw new InvalidOperationException("Formal KV transport required.");
|
||||
loading = true;
|
||||
var transaction = PlayerKVGameplayBindings.Bind(this);
|
||||
coordinator = new PlayerKVGameLoadCoordinator(transaction,
|
||||
() => loading && !closing && !published && handle == null,
|
||||
() => { }, () => intents?.Count ?? 0, server.BindKVForGame, server.ClearKVForGame);
|
||||
server.KVUnauthorized += OnUnauthorized;
|
||||
var identity = new PlayerKVIdentitySnapshot(null, false, PlayerKVIdentityChannel.none,
|
||||
Array.Empty<PlayerKVIdentityChannel>());
|
||||
var snapshot = PlayerKVGameplayBindings.Snapshot(DateTime.UtcNow, "formal-v1");
|
||||
var capability = PlayerKVGameLoadCapability.CreateForGame(identity, snapshot, selection.Api,
|
||||
Application.persistentDataPath, handlerFactory, handlerFactory, handlerFactory);
|
||||
var result = await coordinator.LoadForGameAsync(canonicalId, capability, LoginLifetime,
|
||||
() => !closing && CustomId == requestedId);
|
||||
KVLastError = result.Error;
|
||||
if (result.State != PlayerKVGameLoadState.GameLoaded || closing || CustomId != requestedId ||
|
||||
result.Account == null || result.Account.PlayerId != canonicalId || result.Account.Epoch != result.Epoch ||
|
||||
!coordinator.TryGetCurrentGameHandle(result.Epoch, out var original))
|
||||
{ Logout(); return ELoginStep.Api; }
|
||||
epoch = result.Epoch;
|
||||
handle = original;
|
||||
if (SaveStore is not KVPlayerSaveStore store) throw new InvalidOperationException("KV save store required.");
|
||||
store.Bind(original, LoginLifetime);
|
||||
PrepareProfile(result.Account);
|
||||
BindPromptServices(original);
|
||||
// Chat subscribes before the unique success event. No SDK or analytics initialization here.
|
||||
GContext.container.Resolve<IChatService>().WaitingForLogin();
|
||||
if (!KVHandoffReady || CustomId != requestedId) throw new InvalidOperationException("Stale KV login.");
|
||||
server.EnableKVBusiness(epoch);
|
||||
playerId = result.Account.PlayerId;
|
||||
internalId = result.Account.InternalId;
|
||||
published = true;
|
||||
GContext.Publish(new EvtAPISvrLoginSuccess { playerId = playerId, internalId = internalId });
|
||||
if (!IsAuthorized) return ELoginStep.Api;
|
||||
_ = RunSaveScheduleAsync(store);
|
||||
progress?.Invoke(2);
|
||||
return ELoginStep.Done;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
KVLastError = PlayerKVGameLoadError.Apply;
|
||||
Debug.LogError("KV login failed: " + ex.GetType().Name);
|
||||
Logout();
|
||||
return ELoginStep.Api;
|
||||
}
|
||||
finally { loading = false; }
|
||||
}
|
||||
|
||||
void PrepareProfile(PlayerKVAccountDescriptor account)
|
||||
{
|
||||
if (account.InternalId == 0 || account.CreateTimeUtc == DateTime.MinValue || account.ServerTimeUtc == DateTime.MinValue)
|
||||
throw new InvalidOperationException("Authenticated account metadata required.");
|
||||
CreateTime = account.CreateTimeUtc;
|
||||
LoginTime = account.ServerTimeUtc;
|
||||
var previous = Read("LastLoginTimeUtc");
|
||||
LastLoginTime = ParseLastLoginTime(previous.Value, previous.Found.Value);
|
||||
DisplayName = Read("DisplayName").Value;
|
||||
if (string.IsNullOrEmpty(DisplayName)) DisplayName = GetDefaultName(account.PlayerId);
|
||||
AvatarUrl = Read("AvatarUrl").Value ?? string.Empty;
|
||||
Language = Read("Language").Value;
|
||||
if (string.IsNullOrEmpty(Language)) Language = GContext.container.Resolve<ISettingService>().Lang;
|
||||
var continent = Read("ContinentCode").Value;
|
||||
ContinentCode = new[] { "AF", "AN", "AS", "EU", "NA", "OC", "SA" }.Contains(continent) ? continent : null;
|
||||
}
|
||||
|
||||
internal static DateTime ParseLastLoginTime(string value, bool found)
|
||||
{
|
||||
if (!found) return DateTime.MinValue;
|
||||
if (!DateTime.TryParseExact(value, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var time) ||
|
||||
time == DateTime.MinValue || time.Kind == DateTimeKind.Unspecified)
|
||||
throw new FormatException("Invalid LastLoginTimeUtc.");
|
||||
return time.ToUniversalTime();
|
||||
}
|
||||
|
||||
PlayerSaveReadResult Read(string key)
|
||||
{
|
||||
var result = SaveStore.Read(key);
|
||||
if (!result.Success) throw new InvalidOperationException("KV profile read failed: " + result.Error);
|
||||
return result;
|
||||
}
|
||||
|
||||
void BindPromptServices(PlayerKVGameSessionHandle original)
|
||||
{
|
||||
if (promptsBound || !KVHandoffReady) throw new InvalidOperationException("Prompt binding is stale.");
|
||||
var settings = GContext.container.Resolve<ISettingService>() as DefaultSettingService;
|
||||
var notifications = GContext.container.Resolve<ILocalNotificationService>() as LocalNotificationService;
|
||||
var club = GContext.container.Resolve<ClubService>();
|
||||
if (settings == null || notifications == null || club == null ||
|
||||
!ReferenceEquals(settings, GContext.container.Resolve<ISettingService>()) ||
|
||||
!ReferenceEquals(notifications, GContext.container.Resolve<ILocalNotificationService>()) ||
|
||||
!ReferenceEquals(club, GContext.container.Resolve<ClubService>()))
|
||||
throw new InvalidOperationException("Formal prompt services required.");
|
||||
settings.PreflightFormalPromptOwner(original);
|
||||
club.PreflightFormalPromptOwner(original);
|
||||
notifications.PreflightFormalPromptSettings(settings);
|
||||
promptsBound = true;
|
||||
settings.BindFormalPromptOwner(original);
|
||||
club.BindFormalPromptOwner(original);
|
||||
notifications.BindFormalPromptSettings(settings);
|
||||
if (!settings.HasFormalPromptOwner(original) || !club.HasFormalPromptOwner(original) ||
|
||||
!notifications.HasFormalPromptSettings(settings) || !KVHandoffReady)
|
||||
throw new InvalidOperationException("Formal prompt binding changed.");
|
||||
}
|
||||
|
||||
void OnUnauthorized(int version)
|
||||
{
|
||||
if (version == customServerMgr.SessionVersion) Logout();
|
||||
}
|
||||
|
||||
public void Logout() => ObserveClose(CloseKVForValidationAsync());
|
||||
async void ObserveClose(Task task)
|
||||
{
|
||||
try { await task; }
|
||||
catch (Exception ex) { Debug.LogError("KV close failed: " + ex.GetType().Name); }
|
||||
}
|
||||
internal Task CloseKVForValidationAsync()
|
||||
{
|
||||
if (closeTask != null) return closeTask;
|
||||
closing = true;
|
||||
published = false;
|
||||
(SaveStore as KVPlayerSaveStore)?.Dispose();
|
||||
playerId = null; internalId = 0; handle = null; epoch = 0;
|
||||
DisplayName = AvatarUrl = Language = ContinentCode = null;
|
||||
ClearPlayInfos();
|
||||
Application.quitting -= Logout;
|
||||
if (customServerMgr is CustomServerMgr server)
|
||||
{
|
||||
server.KVUnauthorized -= OnUnauthorized;
|
||||
server.DisableKVBusiness();
|
||||
}
|
||||
// Revoke first; a synchronous cancellation callback must already see invalid identity.
|
||||
closeTask = coordinator?.Revoke() ?? Task.CompletedTask;
|
||||
lifetime.Cancel();
|
||||
return closeTask;
|
||||
}
|
||||
Task IKVUserLifecycle.CloseKVForValidationAsync() => CloseKVForValidationAsync();
|
||||
internal Task<PlayerKVShutdownStatus> ObserveKVQuitAsync() =>
|
||||
coordinator?.ObserveQuitAsync() ?? Task.FromResult(PlayerKVShutdownStatus.Ready);
|
||||
Task<PlayerKVShutdownStatus> IKVUserLifecycle.ObserveKVQuitAsync() => ObserveKVQuitAsync();
|
||||
|
||||
void IKVUserLoadReceiver.ResetForKVLoad()
|
||||
{
|
||||
if ((!loading && !closing) || published || handle != null)
|
||||
throw new InvalidOperationException("KV identity must be unpublished during load/reset.");
|
||||
intents?.Discard(); intents = null;
|
||||
EAccoutUrl = new Dictionary<string, string>(); KVTapJoyBalance = 0;
|
||||
}
|
||||
void IKVUserLoadReceiver.ReplaceFromKVDecoded(PlayerKVLoginState state, long loadEpoch, IReadOnlyDictionary<string, string> raw)
|
||||
{
|
||||
if (!loading || closing || published || handle != null || state == null || raw == null)
|
||||
throw new InvalidOperationException("KV load receiver is not current.");
|
||||
intents = new PlayerKVInitializationIntents(loadEpoch, state.Channel);
|
||||
foreach (var pair in state.Normalizations) intents.Add(loadEpoch, pair.Key, pair.Value);
|
||||
EAccoutUrl = state.AccountUrls.ToDictionary(p => p.Key, p => p.Value, StringComparer.Ordinal);
|
||||
KVTapJoyBalance = state.TapJoyBalance;
|
||||
}
|
||||
void IKVUserLoadReceiver.DiscardKVIntents() => intents?.Discard();
|
||||
|
||||
async Task SaveProfile(string key, string value)
|
||||
{
|
||||
var store = SaveStore;
|
||||
if (!IsAuthorized || store == null) throw new InvalidOperationException("KV identity unavailable.");
|
||||
var result = await store.WriteAndConfirmAsync(key, value, LoginLifetime);
|
||||
if (!IsAuthorized || !ReferenceEquals(store, SaveStore) || !store.IsCurrent || !result.Confirmed)
|
||||
throw new InvalidOperationException("KV profile save was not confirmed.");
|
||||
}
|
||||
|
||||
async Task RunSaveScheduleAsync(IPlayerSaveStore store)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (IsAuthorized && store.IsCurrent)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(40), LoginLifetime);
|
||||
if (!IsAuthorized || !store.IsCurrent) return;
|
||||
var result = await store.FlushAsync(LoginLifetime);
|
||||
if (!result.Confirmed && !LoginLifetime.IsCancellationRequested)
|
||||
Debug.LogWarning("KV scheduled save: " + result.Status);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
catch (Exception ex) { Debug.LogError("KV save scheduler failed: " + ex.GetType().Name); }
|
||||
}
|
||||
public async Task<string> ChangeDisplayName(string newName)
|
||||
{
|
||||
await SaveProfile("DisplayName", newName);
|
||||
DisplayName = newName; GContext.Publish(new ChangeAvatarEvent()); UpdateProfile(); return newName;
|
||||
}
|
||||
public async Task<string> UpdateAvatarUrl(string url)
|
||||
{
|
||||
await SaveProfile("AvatarUrl", url);
|
||||
AvatarUrl = url; GContext.Publish(new ChangeAvatarEvent()); UpdateProfile(); return url;
|
||||
}
|
||||
public async Task<string> UpdateLanguage(string code)
|
||||
{ await SaveProfile("Language", code); Language = code; return code; }
|
||||
public double CreateTotalSeconds() => (ZZTimeHelper.UtcNow() - CreateTime).TotalSeconds;
|
||||
// The non-AGG channel has no account-link operation; never manufacture a successful binding.
|
||||
public Task<(int code, int userid)> LinkAccount(EAccoutType type) => Task.FromResult((1, 0));
|
||||
public Task<bool> GetLinkState(EAccoutType type) => Task.FromResult(false);
|
||||
public string GetDefaultName(string id) => "Angler#" + (string.IsNullOrEmpty(id) ? string.Empty : id.Substring(0, Math.Min(5, id.Length)));
|
||||
public void SetPlayerLv(string id, int level) => levels[id] = id == UserId ? GContext.container.Resolve<PlayerData>().lv : level;
|
||||
public int GetPlayerLv(string id) => id == UserId && IsAuthorized ? GContext.container.Resolve<PlayerData>().lv :
|
||||
id != null && levels.TryGetValue(id, out var level) ? level : -1;
|
||||
public Task<(string, int)> GetPlayLv(string id)
|
||||
{
|
||||
var level = GetPlayerLv(id);
|
||||
return level >= 0 ? Task.FromResult((id, level)) :
|
||||
Task.FromException<(string, int)>(new InvalidOperationException("Public player level has not been loaded."));
|
||||
}
|
||||
public PlayInfo GetPlayInfo(string id)
|
||||
{
|
||||
if (id == UserId && IsAuthorized) return new PlayInfo { name = DisplayName, avatarUrl = AvatarUrl };
|
||||
return id != null && profiles.TryGetValue(id, out var profile) ? profile : null;
|
||||
}
|
||||
public void SetPlayInfo(string id, string name, string avatarUrl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id)) return;
|
||||
profiles[id] = new PlayInfo { name = string.IsNullOrEmpty(name) ? GetDefaultName(id) : name, avatarUrl = avatarUrl };
|
||||
}
|
||||
public void ClearPlayInfos() { profiles.Clear(); levels.Clear(); }
|
||||
public void UpdateProfile()
|
||||
{
|
||||
if (IsAuthorized) customServerMgr.AccountRequest("UpdateProfile", new LoginRequest { playerId = ApiPlayerId,
|
||||
playerProfile = new PlayerProfile { Level = GContext.container.Resolve<PlayerData>().lv, Avatar = AvatarUrl, DisplayName = DisplayName } });
|
||||
}
|
||||
public void UpdateProfileLv()
|
||||
{
|
||||
if (IsAuthorized) customServerMgr.AccountRequest("UpdateProfile", new LoginRequest { playerId = ApiPlayerId,
|
||||
playerProfile = new PlayerProfile { Level = GContext.container.Resolve<PlayerData>().lv } });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
internal interface IKVUserLifecycle
|
||||
{
|
||||
bool KVHandoffReady { get; }
|
||||
PlayerKVGameLoadError KVLastError { get; }
|
||||
Task CloseKVForValidationAsync();
|
||||
Task<PlayerKVShutdownStatus> ObserveKVQuitAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
internal interface IKVUserLoadReceiver
|
||||
{
|
||||
void ResetForKVLoad();
|
||||
void ReplaceFromKVDecoded(PlayerKVLoginState state, long epoch, IReadOnlyDictionary<string, string> raw);
|
||||
void DiscardKVIntents();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
// A store belongs to one login. Callers retain this instance across awaits.
|
||||
public interface IPlayerSaveStore
|
||||
{
|
||||
bool IsCurrent { get; }
|
||||
PlayerSaveReadResult Read(string key);
|
||||
PlayerSaveWriteResult Write(string key, string value, bool remoteStore = true);
|
||||
Task<PlayerSaveConfirmation> WriteAndConfirmAsync(string key, string value, CancellationToken token = default);
|
||||
Task<PlayerSaveConfirmation> ConfirmAsync(PlayerSaveReceipt receipt, CancellationToken token = default);
|
||||
Task<PlayerSaveConfirmation> FlushAsync(CancellationToken token = default);
|
||||
}
|
||||
|
||||
public sealed class PlayerSaveReadResult
|
||||
{
|
||||
public bool Success => Error == null;
|
||||
public bool? Found { get; }
|
||||
public string Value { get; }
|
||||
public string Error { get; }
|
||||
internal PlayerSaveReadResult(bool found, string value) { Found = found; Value = value; }
|
||||
internal PlayerSaveReadResult(string error) { Error = error ?? "ReadFailed"; }
|
||||
public string ValueOrThrow()
|
||||
{
|
||||
if (!Success) throw new InvalidOperationException("Player save read failed: " + Error);
|
||||
return Value;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PlayerSaveReceipt
|
||||
{
|
||||
internal readonly IPlayerSaveStore Store;
|
||||
internal readonly object Ticket;
|
||||
internal PlayerSaveReceipt(IPlayerSaveStore store, object ticket) { Store = store; Ticket = ticket; }
|
||||
}
|
||||
|
||||
public sealed class PlayerSaveWriteResult
|
||||
{
|
||||
public bool Accepted => Error == null;
|
||||
public string Error { get; }
|
||||
public PlayerSaveReceipt Receipt { get; }
|
||||
internal PlayerSaveWriteResult(PlayerSaveReceipt receipt) { Receipt = receipt; }
|
||||
internal PlayerSaveWriteResult(string error) { Error = error ?? "WriteRejected"; }
|
||||
public void RequireAccepted()
|
||||
{
|
||||
if (!Accepted) throw new InvalidOperationException("Player save write rejected: " + Error);
|
||||
}
|
||||
}
|
||||
|
||||
// Buffered and LocalOnly are deliberately different from cloud confirmation.
|
||||
public enum PlayerSaveStatus { Confirmed, Buffered, LocalOnly, Rejected, Superseded, Unknown, Cancelled }
|
||||
public sealed class PlayerSaveConfirmation
|
||||
{
|
||||
public PlayerSaveStatus Status { get; }
|
||||
public bool Confirmed => Status == PlayerSaveStatus.Confirmed;
|
||||
public string Error { get; }
|
||||
internal PlayerSaveConfirmation(PlayerSaveStatus status, string error = null) { Status = status; Error = error; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
internal enum PlayerKVGameLoadError
|
||||
{ None, BackendNotEnabled, BackendUnsupported, Input, Prepare, Schema, Apply, Stale, SideEffect, GateBusy, PreviousClosePending, PreviousCloseFailed, ClosePending, CloseFailed }
|
||||
internal enum PlayerKVGameLoadState { Rejected, LoadedForValidation, GameLoaded }
|
||||
// Preserve stage evidence without retaining a successful Prepare payload on a later load failure.
|
||||
internal sealed class PlayerKVPrepareEvidence
|
||||
{
|
||||
internal PlayerKVLoginPrepareError Error { get; }
|
||||
internal PlayerKVLoginPrepareError OriginalError { get; }
|
||||
internal game.Account.AccountLoginError? AccountError { get; }
|
||||
internal PlayerKVGameOpenError? OpenError { get; }
|
||||
internal PlayerKVSessionOpenError? OwnerError { get; }
|
||||
internal PlayerKVError? KVError { get; }
|
||||
internal PlayerKVSyncStatus? SyncStatus { get; }
|
||||
internal int? HttpStatusCode { get; }
|
||||
internal string ExceptionType { get; }
|
||||
internal string CleanupExceptionType { get; }
|
||||
internal PlayerKVPrepareEvidence(PlayerKVLoginPrepareResult value)
|
||||
{ Error=value.Error; OriginalError=value.OriginalError; AccountError=value.AccountError; OpenError=value.OpenError;
|
||||
OwnerError=value.OwnerError; KVError=value.KVError; SyncStatus=value.SyncStatus; HttpStatusCode=value.HttpStatusCode;
|
||||
ExceptionType=value.ExceptionType; CleanupExceptionType=value.CleanupExceptionType; }
|
||||
}
|
||||
internal sealed class PlayerKVGameLoadResult
|
||||
{
|
||||
internal PlayerKVGameLoadState State { get; }
|
||||
internal PlayerKVGameLoadError Error { get; }
|
||||
internal PlayerKVGameLoadError OriginalError { get; }
|
||||
internal bool CleanupFailed => Error==PlayerKVGameLoadError.CloseFailed;
|
||||
internal PlayerKVSchemaVerdict? SchemaVerdict { get; }
|
||||
internal PlayerKVPrepareEvidence Prepare { get; }
|
||||
internal long Epoch { get; }
|
||||
internal PlayerKVAccountDescriptor Account { get; }
|
||||
internal int ModuleCount { get; }
|
||||
internal int IntentCount { get; }
|
||||
internal PlayerKVGameLoadResult(PlayerKVGameLoadError error,long epoch,PlayerKVGameLoadError? original=null,
|
||||
PlayerKVSchemaVerdict? verdict=null,PlayerKVLoginPrepareResult prepare=null,int modules=0,int intents=0,bool forGame=false,PlayerKVAccountDescriptor account=null)
|
||||
{ Error=error; OriginalError=original ?? error; SchemaVerdict=verdict; Prepare=prepare==null ? null : new PlayerKVPrepareEvidence(prepare); Epoch=epoch; ModuleCount=modules; IntentCount=intents;
|
||||
Account=error==PlayerKVGameLoadError.None && forGame ? account : null;
|
||||
State=error!=PlayerKVGameLoadError.None ? PlayerKVGameLoadState.Rejected : forGame ? PlayerKVGameLoadState.GameLoaded : PlayerKVGameLoadState.LoadedForValidation; }
|
||||
}
|
||||
internal sealed class PlayerKVGameLoadCoordinator
|
||||
{
|
||||
readonly SemaphoreSlim loginGate=new SemaphoreSlim(1,1);
|
||||
readonly object closeGate=new object();
|
||||
readonly PlayerKVGameLoadTransaction transaction;
|
||||
readonly Func<bool> unstarted;
|
||||
readonly Action notify;
|
||||
readonly Func<int> intentCount;
|
||||
readonly Action<PlayerKVAccountLease> bind;
|
||||
readonly Func<Task> unbind;
|
||||
readonly PlayerKVShutdownRegistry registry;
|
||||
readonly int mainThread;
|
||||
readonly SynchronizationContext mainContext;
|
||||
PlayerKVLoginContext context;
|
||||
PlayerKVGameLoadGuard guard;
|
||||
PlayerKVGameLoadCapability capability;
|
||||
Task closing;
|
||||
CancellationTokenRegistration lifetimeRegistration;
|
||||
Action clearAfterApply;
|
||||
long epoch;
|
||||
bool loaded;
|
||||
PlayerKVGameSessionHandle gameHandle;
|
||||
internal bool LoadedForValidation => loaded && context!=null && context.IsCurrent && guard!=null && guard.IsCurrent && guard.Violations==0;
|
||||
internal int QuitObserveMs => capability?.QuitObserveMs ?? 5000;
|
||||
internal PlayerKVGameLoadCoordinator(PlayerKVGameLoadTransaction transaction,Func<bool> unstarted,Action notify,Func<int> intentCount,
|
||||
Action<PlayerKVAccountLease> bind,Func<Task> unbind,PlayerKVShutdownRegistry registry=null)
|
||||
{
|
||||
this.transaction=transaction ?? throw new ArgumentNullException(nameof(transaction));
|
||||
this.unstarted=unstarted ?? throw new ArgumentNullException(nameof(unstarted)); this.notify=notify ?? throw new ArgumentNullException(nameof(notify));
|
||||
this.intentCount=intentCount ?? throw new ArgumentNullException(nameof(intentCount)); this.bind=bind ?? throw new ArgumentNullException(nameof(bind));
|
||||
this.unbind=unbind ?? throw new ArgumentNullException(nameof(unbind)); this.registry=registry ?? PlayerKVShutdownRegistry.Process;
|
||||
mainThread=Thread.CurrentThread.ManagedThreadId; mainContext=SynchronizationContext.Current;
|
||||
}
|
||||
internal Task<PlayerKVGameLoadResult> LoadAsync(string id,PlayerKVGameLoadCapability next,CancellationToken token=default,Func<bool> identityCurrent=null)
|
||||
=> LoadCoreAsync(id,next,token,identityCurrent,false);
|
||||
internal Task<PlayerKVGameLoadResult> LoadForGameAsync(string id,PlayerKVGameLoadCapability next,CancellationToken token=default,Func<bool> identityCurrent=null)
|
||||
=> LoadCoreAsync(id,next,token,identityCurrent,true);
|
||||
internal bool TryGetCurrentGameHandle(long expectedEpoch,out PlayerKVGameSessionHandle handle)
|
||||
{
|
||||
handle=null;
|
||||
PlayerKVLoginContext owner; PlayerKVGameSessionHandle published;
|
||||
lock(closeGate)
|
||||
{
|
||||
if(closing!=null || epoch!=expectedEpoch || gameHandle==null) return false;
|
||||
owner=context; published=gameHandle;
|
||||
}
|
||||
// Never call the external identity predicate while holding a publication monitor.
|
||||
if(!owner.TryAcquireGameHandle(expectedEpoch,out var original) || !ReferenceEquals(original,published)) return false;
|
||||
lock(closeGate)
|
||||
{
|
||||
if(closing!=null || epoch!=expectedEpoch || !ReferenceEquals(context,owner) || !ReferenceEquals(gameHandle,published)) return false;
|
||||
handle=published;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
async Task<PlayerKVGameLoadResult> LoadCoreAsync(string id,PlayerKVGameLoadCapability next,CancellationToken token,Func<bool> identityCurrent,bool forGame)
|
||||
{
|
||||
if(next==null || Thread.CurrentThread.ManagedThreadId!=mainThread || string.IsNullOrEmpty(id))
|
||||
return Result(PlayerKVGameLoadError.Input);
|
||||
if(!await EnterGate(next,token)) return Result(token.IsCancellationRequested ? PlayerKVGameLoadError.Stale : PlayerKVGameLoadError.GateBusy);
|
||||
PlayerKVLoginPrepareResult prepared=null; PlayerKVSchemaVerdict? verdict=null;
|
||||
long attempt=0;
|
||||
try
|
||||
{
|
||||
// An earlier successful validation must also be explicitly revoked before replacing its account.
|
||||
if(context!=null) _=Revoke();
|
||||
var previous=await registry.ObserveAsync(next.LoginObserveMs,token,next.Delay);
|
||||
if(previous!=PlayerKVShutdownStatus.Ready) return Result(previous==PlayerKVShutdownStatus.Failed ? PlayerKVGameLoadError.PreviousCloseFailed :
|
||||
previous==PlayerKVShutdownStatus.Cancelled ? PlayerKVGameLoadError.Stale : PlayerKVGameLoadError.PreviousClosePending);
|
||||
if(token.IsCancellationRequested || !unstarted() || transaction.Poisoned) return Result(PlayerKVGameLoadError.Input);
|
||||
if(!PlayerKVGameLoadGuard.TryEnter(out var enteredGuard)) return Result(PlayerKVGameLoadError.GateBusy);
|
||||
lifetimeRegistration.Dispose();
|
||||
lock(closeGate)
|
||||
{
|
||||
guard=enteredGuard; capability=next; closing=null; loaded=false; gameHandle=null;
|
||||
attempt=Interlocked.Increment(ref epoch);
|
||||
context=next.CreateContext(id,attempt,e=>Interlocked.Read(ref epoch)==e && !token.IsCancellationRequested && (identityCurrent==null || identityCurrent()));
|
||||
}
|
||||
var owner=context;
|
||||
lifetimeRegistration=token.Register(()=> { _=Revoke(attempt); });
|
||||
var work=context.PrepareAsync(token);
|
||||
var cancelled=new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
using(token.Register(()=>cancelled.TrySetResult(true)))
|
||||
{
|
||||
if(await Task.WhenAny(work,cancelled.Task)!=work)
|
||||
return await Reject(PlayerKVGameLoadError.Stale,attempt,next,prepared,verdict);
|
||||
prepared=await work;
|
||||
}
|
||||
if(!prepared.Prepared) return await Reject(prepared.CleanupFailed ? PlayerKVGameLoadError.CloseFailed : PlayerKVGameLoadError.Prepare,attempt,next,prepared,verdict);
|
||||
if(!context.IsCurrent || epoch!=attempt) return await Reject(PlayerKVGameLoadError.Stale,attempt,next,prepared,verdict);
|
||||
if(next.RequireExistingArchive && (context.AccountDescriptor?.NewlyCreated!=false || prepared.Data==null || prepared.Data.Count==0))
|
||||
return await Reject(PlayerKVGameLoadError.Schema,attempt,next,prepared,verdict);
|
||||
var decoded=PlayerKVSaveSchema.Decode(prepared,context.AccountDescriptor,id,attempt,context.IsCurrent,next.Snapshot,next.Snapshot.Version,next.Identity);
|
||||
verdict=decoded.Verdict;
|
||||
if(decoded.Data==null) return await Reject(PlayerKVGameLoadError.Schema,attempt,next,prepared,verdict);
|
||||
if(!context.TryAcquireAccountLease(next.LeaseCapability,out var lease)) return await Reject(PlayerKVGameLoadError.Stale,attempt,next,prepared,verdict);
|
||||
bind(lease);
|
||||
PlayerKVApplyError applied;
|
||||
try { applied=transaction.Apply(decoded.Data,()=>context.IsCurrent && epoch==attempt && unstarted(),()=>guard.IsCurrent && guard.Violations==0,notify); }
|
||||
finally { var pending=clearAfterApply; clearAfterApply=null; pending?.Invoke(); }
|
||||
if(applied!=PlayerKVApplyError.None) return await Reject(applied==PlayerKVApplyError.Stale ? PlayerKVGameLoadError.Stale :
|
||||
applied==PlayerKVApplyError.SideEffect ? PlayerKVGameLoadError.SideEffect : PlayerKVGameLoadError.Apply,attempt,next,prepared,verdict);
|
||||
if(token.IsCancellationRequested || !owner.IsCurrent || enteredGuard.Violations!=0) return await Reject(PlayerKVGameLoadError.Stale,attempt,next,prepared,verdict);
|
||||
var intents=intentCount();
|
||||
PlayerKVGameSessionHandle original=null;
|
||||
if(forGame && !owner.TryAcquireGameHandle(attempt,out original)) return await Reject(PlayerKVGameLoadError.Stale,attempt,next,prepared,verdict);
|
||||
bool published=false;
|
||||
lock(closeGate)
|
||||
{
|
||||
if(closing==null && epoch==attempt && !token.IsCancellationRequested && ReferenceEquals(context,owner) && enteredGuard.IsCurrent && enteredGuard.Violations==0)
|
||||
{
|
||||
if(forGame)
|
||||
{
|
||||
enteredGuard.Dispose();
|
||||
// Dispose serializes with Reject; its final count cannot change afterwards.
|
||||
if(enteredGuard.Violations==0) { gameHandle=original; published=true; }
|
||||
}
|
||||
else { loaded=true; published=true; }
|
||||
}
|
||||
}
|
||||
if(!published) return await Reject(PlayerKVGameLoadError.Stale,attempt,next,prepared,verdict);
|
||||
return new PlayerKVGameLoadResult(PlayerKVGameLoadError.None,attempt,verdict:verdict,modules:31,intents:intents,forGame:forGame,account:context.AccountDescriptor);
|
||||
}
|
||||
catch { return await Reject(PlayerKVGameLoadError.Apply,attempt,next,prepared,verdict); }
|
||||
finally { loginGate.Release(); }
|
||||
}
|
||||
async Task<bool> EnterGate(PlayerKVGameLoadCapability next,CancellationToken token)
|
||||
{
|
||||
using(var cancellation=CancellationTokenSource.CreateLinkedTokenSource(token))
|
||||
{
|
||||
var wait=loginGate.WaitAsync(cancellation.Token); var deadline=next.Delay(next.LoginObserveMs,cancellation.Token);
|
||||
bool acquired=false,accepted=false;
|
||||
try
|
||||
{
|
||||
if(await Task.WhenAny(wait,deadline)==wait) { await wait; acquired=true; accepted=!token.IsCancellationRequested && !deadline.IsCompleted; return accepted; }
|
||||
cancellation.Cancel();
|
||||
try { await wait; acquired=true; } catch(OperationCanceledException) { }
|
||||
return false;
|
||||
}
|
||||
catch(OperationCanceledException) { return false; }
|
||||
finally
|
||||
{
|
||||
cancellation.Cancel();
|
||||
// A lock won at the timeout/cancel boundary belongs to this attempt and must be returned.
|
||||
if(acquired && !accepted) loginGate.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
internal Task Revoke(long? expectedEpoch=null)
|
||||
{
|
||||
lock(closeGate)
|
||||
{
|
||||
if(expectedEpoch.HasValue && expectedEpoch.Value!=Interlocked.Read(ref epoch)) return Task.CompletedTask;
|
||||
if(closing!=null) return closing;
|
||||
loaded=false; gameHandle=null; Interlocked.Increment(ref epoch);
|
||||
if(context==null && guard==null) return Task.CompletedTask;
|
||||
var completion=new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
closing=completion.Task;
|
||||
var owner=context; var leaseGuard=guard; var source=capability;
|
||||
// Context cancellation is synchronous and occurs even if the main-thread reset is queued.
|
||||
var drain=owner?.CloseAsync() ?? Task.CompletedTask;
|
||||
var cleared=new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
void Clear()
|
||||
{
|
||||
try
|
||||
{
|
||||
var detached=unbind();
|
||||
if(!transaction.Clear()) throw new InvalidOperationException("Controlled container is poisoned.");
|
||||
CompleteClear(detached,cleared);
|
||||
}
|
||||
catch(Exception ex) { cleared.TrySetException(ex); }
|
||||
}
|
||||
if(Thread.CurrentThread.ManagedThreadId==mainThread && transaction.IsApplying) clearAfterApply=Clear;
|
||||
else if(Thread.CurrentThread.ManagedThreadId==mainThread) Clear();
|
||||
else if(mainContext!=null) mainContext.Post(_=>Clear(),null);
|
||||
else cleared.TrySetException(new InvalidOperationException("Main-thread cleanup unavailable."));
|
||||
_=FinishClose(drain,cleared.Task,leaseGuard,source,completion);
|
||||
registry.Register(drain,owner);
|
||||
registry.Register(cleared.Task,owner);
|
||||
registry.Register(closing,owner);
|
||||
return closing;
|
||||
}
|
||||
}
|
||||
static async void CompleteClear(Task detached,TaskCompletionSource<bool> cleared)
|
||||
{ try { await detached; cleared.TrySetResult(true); } catch(Exception ex) { cleared.TrySetException(ex); } }
|
||||
static async Task FinishClose(Task drain,Task clear,PlayerKVGameLoadGuard leaseGuard,PlayerKVGameLoadCapability source,TaskCompletionSource<bool> completion)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(drain,clear).ConfigureAwait(false); source?.CloseUnused(); leaseGuard?.Dispose(); completion.TrySetResult(true);
|
||||
}
|
||||
catch(Exception ex) { completion.TrySetException(ex); }
|
||||
}
|
||||
async Task<PlayerKVGameLoadResult> Reject(PlayerKVGameLoadError original,long attempt,PlayerKVGameLoadCapability source,
|
||||
PlayerKVLoginPrepareResult prepare,PlayerKVSchemaVerdict? verdict)
|
||||
{
|
||||
var close=Revoke();
|
||||
var status=await registry.ObserveAsync(source.LoginObserveMs,CancellationToken.None,source.Delay);
|
||||
var error=status==PlayerKVShutdownStatus.Failed || close.IsFaulted ? PlayerKVGameLoadError.CloseFailed :
|
||||
!close.IsCompleted ? PlayerKVGameLoadError.ClosePending : original;
|
||||
return new PlayerKVGameLoadResult(error,attempt,original:prepare?.CleanupFailed==true ? PlayerKVGameLoadError.Prepare : original,verdict:verdict,prepare:prepare);
|
||||
}
|
||||
PlayerKVGameLoadResult Result(PlayerKVGameLoadError error) => new PlayerKVGameLoadResult(error,Interlocked.Read(ref epoch));
|
||||
internal Task<PlayerKVShutdownStatus> ObserveQuitAsync() => registry.ObserveAsync(QuitObserveMs,delay:capability?.Delay);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#if !SERVICE_HOST
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using asap.core;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
// Bind the existing game container. Missing registrations fail closed; no parallel fixture objects.
|
||||
internal static class PlayerKVGameplayBindings
|
||||
{
|
||||
static T Require<T>() where T:class => GContext.container.Resolve<T>() ??
|
||||
throw new InvalidOperationException("KV game module is not registered: "+typeof(T).FullName);
|
||||
internal static PlayerKVSchemaSnapshot Snapshot(DateTime nowUtc,string version="kv-loop")
|
||||
{
|
||||
var tables=Require<cfg.Tables>();
|
||||
return new PlayerKVSchemaSnapshot(version,tables.TbRodData.DataMap.Keys,tables.TbMapData.DataMap.Keys,
|
||||
Enum.GetNames(typeof(cfg.ConditionType)).ToDictionary(n=>n,n=>(int)Enum.Parse(typeof(cfg.ConditionType),n)),CultureInfo.InvariantCulture,
|
||||
nowUtc,tables.TbGlobalConfig.InitWheelTicket,
|
||||
tables.TbEventPackManager.DataMap.ToDictionary(p=>p.Key,p=>p.Value.PackType),
|
||||
tables.TbFishingEvent.DataMap.ToDictionary(p=>p.Key,p=>new PlayerKVEventConfig(p.Value.RedirectID,(p.Value.TimeDefinition as cfg.LimitedTime)?.EndTime)),tables.TbGlobalConfig.GlobalTimeOffset,
|
||||
tables.TbFishData.DataMap.ToDictionary(p=>p.Key,p=>new PlayerKVFishConfig(p.Key,p.Value.AreaID,p.Value.Quality,p.Value.FishCardID,p.Value.BaseWeight,p.Value.PointExtraParam,p.Value.WeightRandom)),
|
||||
tables.TbFishCard.DataMap.ToDictionary(p=>p.Key,p=>new PlayerKVFishCardConfig(p.Key,p.Value.MaxLevel,p.Value.CardsRequiredList,p.Value.WeightMagList)));
|
||||
}
|
||||
internal static PlayerKVGameLoadTransaction Bind(IKVUserLoadReceiver user)
|
||||
{
|
||||
var player=Require<GameCore.PlayerData>(); var small=Require<GameCore.SmallGameData>(); var fish=Require<GameCore.PlayerFishData>();
|
||||
var shop=Require<GameCore.PlayerShopData>(); var eventBase=Require<GameCore.FishingEventData>(); var buffs=Require<BuffDataCenter>(); var camp=Require<CampDataMM>();
|
||||
var achievements=Require<AchievementDataManager>(); var box=Require<FishingBoxData>(); var guide=Require<GameCore.GuideDataCenter>(); var battle=Require<BattlePassData>();
|
||||
var sign=Require<GameCore.SigninData>(); var trigger=Require<GameCore.TriggerPackData>(); var club=Require<ClubService>(); var challenge=Require<FishingChallengeManager>();
|
||||
var chains=Require<DataCenter.OfferChainsChestManager>(); var disco=Require<DataCenter.OfferDiscoPartyManager>(); var partner=Require<DataCenter.EventPartnerGatherManager>(); var cube=Require<EventCubeMeltManager>();
|
||||
var eventPack=Require<EventPackData>(); var piggy=Require<PiggyBankPackData>(); var bargain=Require<BargainPackData>(); var foot=Require<IFootPrintService>() as FootPrintService ?? throw new InvalidOperationException("M23 FootPrint binding mismatch."); var shooting=Require<EventShootingRangeData>();
|
||||
var potion=Require<EventPotionDataManager>(); var pinball=Require<EventPinballDataManager>(); var scratch=Require<EventScratchTicketDataManager>();
|
||||
var progress=eventBase.ThanksGivingPackData; var migration=eventBase.ChainPackWithProgressMigrationData;
|
||||
var pinata=Require<EventPinataPackDataManager>(); var digging=Require<Script.RuntimeScript.DiggingGameManager>();
|
||||
var modules=new List<IPlayerKVLoadModule>();
|
||||
void Add<T>(int id,Action reset,Action<T> replace) where T:class => modules.Add(new PlayerKVLoadModule(id,reset,d=>replace(d.Module<T>(id))));
|
||||
Add<PlayerKVPlayerState>(1,player.ResetForKVLoad,player.ReplaceFromKVDecoded); Add<PlayerKVCoreState>(2,small.ResetForKVLoad,small.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVFishState>(3,fish.ResetForKVLoad,fish.ReplaceFromKVDecoded); Add<PlayerKVShopState>(4,shop.ResetForKVLoad,shop.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVEventBaseState>(5,eventBase.ResetForKVLoad,eventBase.ReplaceFromKVDecoded); Add<PlayerKVBuffState>(6,buffs.ResetForKVLoad,buffs.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVCampState>(7,camp.ResetForKVLoad,camp.ReplaceFromKVDecoded); Add<PlayerKVAchievementState>(8,achievements.ResetForKVLoad,achievements.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVIntMapState>(9,box.ResetForKVLoad,box.ReplaceFromKVDecoded); Add<PlayerKVGuideState>(10,guide.ResetForKVLoad,guide.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVBattlePassState>(11,battle.ResetForKVLoad,battle.ReplaceFromKVDecoded); Add<PlayerKVMonthlySignState>(12,sign.ResetForKVLoad,sign.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVTriggerState>(13,trigger.ResetForKVLoad,trigger.ReplaceFromKVDecoded); Add<PlayerKVClubState>(14,club.ResetForKVLoad,club.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVChallengeState>(15,challenge.ResetForKVLoad,challenge.ReplaceFromKVDecoded); Add<PlayerKVOfferState>(16,chains.ResetForKVLoad,chains.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVOfferState>(17,disco.ResetForKVLoad,disco.ReplaceFromKVDecoded); Add<PlayerKVPartnerState>(18,partner.ResetForKVLoad,partner.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVCubeState>(19,cube.ResetForKVLoad,cube.ReplaceFromKVDecoded); Add<PlayerKVEventPackState>(20,eventPack.ResetForKVLoad,eventPack.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVPiggyBankState>(21,piggy.ResetForKVLoad,piggy.ReplaceFromKVDecoded); Add<PlayerKVBargainState>(22,bargain.ResetForKVLoad,bargain.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVFootPrintState>(23,foot.ResetForKVLoad,foot.ReplaceFromKVDecoded); Add<PlayerKVShootingRangeState>(24,shooting.ResetForKVLoad,shooting.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVPotionState>(25,potion.ResetForKVLoad,potion.ReplaceFromKVDecoded); Add<PlayerKVPinballState>(26,pinball.ResetForKVLoad,pinball.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVScratchState>(27,scratch.ResetForKVLoad,scratch.ReplaceFromKVDecoded);
|
||||
Add<PlayerKVProgressPackPair>(28,()=>{progress.ResetForKVLoad();migration.ResetForKVLoad();},p=>{progress.ReplaceFromKVDecoded(p.Current);migration.ReplaceFromKVDecoded(p.Migration);});
|
||||
Add<PlayerKVPinataState>(29,pinata.ResetForKVLoad,pinata.ReplaceFromKVDecoded); Add<PlayerKVEmptyModuleState>(30,digging.ResetForKVLoad,digging.ReplaceFromKVDecoded);
|
||||
modules.Add(new PlayerKVLoadModule(31,user.ResetForKVLoad,d=>user.ReplaceFromKVDecoded(d.Module<PlayerKVLoginState>(31),d.Epoch,d.Raw)));
|
||||
return new PlayerKVGameLoadTransaction(modules,System.Threading.Thread.CurrentThread.ManagedThreadId,user.DiscardKVIntents);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,272 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
// Collection is limited to a synchronous gameplay fragment. It never flows across await.
|
||||
public sealed class PlayerSaveAction
|
||||
{
|
||||
[ThreadStatic] static PlayerSaveAction current;
|
||||
readonly KVPlayerSaveStore store;
|
||||
readonly Dictionary<string, PlayerSaveReceipt> receipts = new Dictionary<string, PlayerSaveReceipt>(StringComparer.Ordinal);
|
||||
readonly List<Action> presentations = new List<Action>();
|
||||
string failure;
|
||||
|
||||
PlayerSaveAction(KVPlayerSaveStore store) { this.store = store; }
|
||||
|
||||
internal static bool Accepts(KVPlayerSaveStore store)
|
||||
{
|
||||
if (current == null || ReferenceEquals(current.store, store)) return true;
|
||||
current.failure = "ForeignStoreInReward";
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static void Record(KVPlayerSaveStore store, string key, PlayerSaveWriteResult result)
|
||||
{
|
||||
if (current == null || !ReferenceEquals(current.store, store)) return;
|
||||
if (!result.Accepted) current.failure = result.Error;
|
||||
else current.receipts[key] = result.Receipt;
|
||||
}
|
||||
|
||||
public static void Present(Action presentation)
|
||||
{
|
||||
if (current == null) presentation();
|
||||
else current.presentations.Add(presentation);
|
||||
}
|
||||
|
||||
public static async Task RunAsync(IPlayerSaveStore store, string actionKey, Action apply,
|
||||
Action savePlayer, CancellationToken token = default)
|
||||
{
|
||||
if (!store.IsCurrent || token.IsCancellationRequested) throw new OperationCanceledException();
|
||||
if (!(store is KVPlayerSaveStore kv)) { apply(); return; }
|
||||
// A live login may still have an UnknownOutcome/Guarded owner. Check readiness before inventory mutation.
|
||||
kv.Read("NewPlayerDataDic").ValueOrThrow();
|
||||
if (current != null) throw new InvalidOperationException("Reward action must be awaited by its caller.");
|
||||
if (!kv.BeginAction(actionKey)) throw new InvalidOperationException("Reward is pending or requires recovery.");
|
||||
var action = new PlayerSaveAction(kv);
|
||||
try
|
||||
{
|
||||
current = action;
|
||||
apply();
|
||||
savePlayer();
|
||||
}
|
||||
finally { current = null; }
|
||||
// Keep the action reserved after any failure: replaying rewards is not recovery.
|
||||
if (action.failure != null) throw new InvalidOperationException("Reward write rejected: " + action.failure);
|
||||
if (action.receipts.Count == 0) throw new InvalidOperationException("Reward produced no save receipts.");
|
||||
foreach (var receipt in action.receipts.Values)
|
||||
{
|
||||
var result = await store.ConfirmAsync(receipt, token);
|
||||
if (!result.Confirmed) throw new InvalidOperationException("Reward save not confirmed: " + result.Status + "/" + result.Error);
|
||||
}
|
||||
if (!store.IsCurrent || token.IsCancellationRequested) throw new OperationCanceledException();
|
||||
// Presentation failure must not cause an already committed reward to replay.
|
||||
foreach (var presentation in action.presentations)
|
||||
{
|
||||
if (!store.IsCurrent || token.IsCancellationRequested) throw new OperationCanceledException();
|
||||
presentation();
|
||||
}
|
||||
kv.CompleteAction(actionKey);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class KVPlayerSaveStore : IPlayerSaveStore, IDisposable
|
||||
{
|
||||
PlayerKVGameSessionHandle handle;
|
||||
CancellationToken lifetime;
|
||||
bool bound, disposed;
|
||||
readonly object receiptsGate = new object();
|
||||
readonly Dictionary<string, PlayerKVSyncTicket> pendingReceipts = new Dictionary<string, PlayerKVSyncTicket>(StringComparer.Ordinal);
|
||||
readonly HashSet<string> actions = new HashSet<string>(StringComparer.Ordinal);
|
||||
internal bool BeginAction(string key) { lock (receiptsGate) return IsCurrent && actions.Add(key); }
|
||||
internal void CompleteAction(string key) { lock (receiptsGate) actions.Remove(key); }
|
||||
public bool IsCurrent => bound && !disposed && !lifetime.IsCancellationRequested && handle.Status != PlayerKVSyncStatus.Closed;
|
||||
|
||||
internal void Bind(PlayerKVGameSessionHandle original, CancellationToken loginLifetime)
|
||||
{
|
||||
if (bound || disposed || original == null || loginLifetime.IsCancellationRequested)
|
||||
throw new InvalidOperationException("Player save store cannot be rebound.");
|
||||
// This also checks the load guard before permitting business use.
|
||||
if (!KVMgr.ReadKV(original, "LastLoginTimeUtc").Success)
|
||||
throw new InvalidOperationException("Player save handle is not ready.");
|
||||
handle = original;
|
||||
lifetime = loginLifetime;
|
||||
bound = true;
|
||||
}
|
||||
|
||||
public PlayerSaveReadResult Read(string key)
|
||||
{
|
||||
if (!IsCurrent) return new PlayerSaveReadResult("StaleOrNotReady");
|
||||
var result = KVMgr.ReadKV(handle, key);
|
||||
return result.Success ? new PlayerSaveReadResult(result.Found.Value, result.Value) :
|
||||
new PlayerSaveReadResult(result.Error.ToString());
|
||||
}
|
||||
|
||||
public PlayerSaveWriteResult Write(string key, string value, bool remoteStore = true)
|
||||
{
|
||||
lock (receiptsGate)
|
||||
{
|
||||
if (!PlayerSaveAction.Accepts(this)) return new PlayerSaveWriteResult("ForeignStoreInReward");
|
||||
if (!IsCurrent)
|
||||
{
|
||||
var rejected = new PlayerSaveWriteResult("StaleOrNotReady");
|
||||
PlayerSaveAction.Record(this, key, rejected);
|
||||
return rejected;
|
||||
}
|
||||
var result = KVMgr.WriteKV(handle, key, value, remoteStore);
|
||||
if (result.Accepted && result.Ticket != null) pendingReceipts[key] = result.Ticket;
|
||||
var write = result.Accepted ? new PlayerSaveWriteResult(new PlayerSaveReceipt(this, result.Ticket)) :
|
||||
new PlayerSaveWriteResult(result.StateResult.Error.ToString());
|
||||
PlayerSaveAction.Record(this, key, write);
|
||||
return write;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PlayerSaveConfirmation> WriteAndConfirmAsync(string key, string value, CancellationToken token = default)
|
||||
{
|
||||
if (token.IsCancellationRequested) return Cancelled();
|
||||
var write = Write(key, value);
|
||||
if (!write.Accepted) return new PlayerSaveConfirmation(PlayerSaveStatus.Rejected, write.Error);
|
||||
return await ConfirmAsync(write.Receipt, token);
|
||||
}
|
||||
|
||||
public async Task<PlayerSaveConfirmation> ConfirmAsync(PlayerSaveReceipt receipt, CancellationToken token = default)
|
||||
{
|
||||
if (!IsCurrent || token.IsCancellationRequested) return Cancelled();
|
||||
if (receipt == null || !ReferenceEquals(receipt.Store, this))
|
||||
return new PlayerSaveConfirmation(PlayerSaveStatus.Rejected, "ForeignReceipt");
|
||||
if (receipt.Ticket == null) return new PlayerSaveConfirmation(PlayerSaveStatus.LocalOnly);
|
||||
using (var wait = CancellationTokenSource.CreateLinkedTokenSource(lifetime, token))
|
||||
{
|
||||
try
|
||||
{
|
||||
await KVMgr.FlushKVAsync(handle, wait.Token);
|
||||
var ticket = await KVMgr.WaitForKVTicketAsync(handle, (PlayerKVSyncTicket)receipt.Ticket, wait.Token);
|
||||
if (!IsCurrent || wait.IsCancellationRequested) return Cancelled();
|
||||
if (ticket.Status == PlayerKVTicketStatus.Confirmed)
|
||||
{
|
||||
ForgetConfirmed((PlayerKVSyncTicket)receipt.Ticket);
|
||||
return new PlayerSaveConfirmation(PlayerSaveStatus.Confirmed);
|
||||
}
|
||||
return new PlayerSaveConfirmation(ticket.Status == PlayerKVTicketStatus.Unknown ? PlayerSaveStatus.Unknown :
|
||||
ticket.Status == PlayerKVTicketStatus.Superseded ? PlayerSaveStatus.Superseded :
|
||||
ticket.Status == PlayerKVTicketStatus.Cancelled ? PlayerSaveStatus.Cancelled : PlayerSaveStatus.Rejected,
|
||||
ticket.TransportError.ToString());
|
||||
}
|
||||
catch (OperationCanceledException) { return Cancelled(); }
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PlayerSaveConfirmation> FlushAsync(CancellationToken token = default)
|
||||
{
|
||||
if (!IsCurrent || token.IsCancellationRequested) return Cancelled();
|
||||
PlayerKVSyncTicket[] captured;
|
||||
lock (receiptsGate) captured = pendingReceipts.Values.ToArray();
|
||||
using (var wait = CancellationTokenSource.CreateLinkedTokenSource(lifetime, token))
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await KVMgr.FlushKVAsync(handle, wait.Token);
|
||||
if (!IsCurrent || wait.IsCancellationRequested) return Cancelled();
|
||||
var status = result.Unknown > 0 || result.CacheStatus == PlayerKVSyncStatus.UnknownOutcome ?
|
||||
PlayerSaveStatus.Unknown : result.Blocked > 0 || result.TransportError != PlayerKVError.None ||
|
||||
result.CacheStatus != PlayerKVSyncStatus.Ready ? PlayerSaveStatus.Rejected : PlayerSaveStatus.Confirmed;
|
||||
if (status != PlayerSaveStatus.Confirmed) return new PlayerSaveConfirmation(status, result.TransportError.ToString());
|
||||
// Multiple writes before this flush are covered by the captured final receipt.
|
||||
// A replacement during the await belongs to a later flush and cannot confirm it.
|
||||
foreach (var receipt in captured)
|
||||
{
|
||||
var ticket = await KVMgr.WaitForKVTicketAsync(handle, receipt, wait.Token);
|
||||
if (!IsCurrent || wait.IsCancellationRequested) return Cancelled();
|
||||
if (ticket.Status != PlayerKVTicketStatus.Confirmed)
|
||||
return new PlayerSaveConfirmation(ticket.Status == PlayerKVTicketStatus.Superseded ? PlayerSaveStatus.Superseded :
|
||||
ticket.Status == PlayerKVTicketStatus.Unknown ? PlayerSaveStatus.Unknown :
|
||||
ticket.Status == PlayerKVTicketStatus.Cancelled ? PlayerSaveStatus.Cancelled : PlayerSaveStatus.Rejected,
|
||||
ticket.TransportError.ToString());
|
||||
ForgetConfirmed(receipt);
|
||||
}
|
||||
return new PlayerSaveConfirmation(PlayerSaveStatus.Confirmed);
|
||||
}
|
||||
catch (OperationCanceledException) { return Cancelled(); }
|
||||
}
|
||||
}
|
||||
|
||||
static PlayerSaveConfirmation Cancelled() => new PlayerSaveConfirmation(PlayerSaveStatus.Cancelled);
|
||||
void ForgetConfirmed(PlayerKVSyncTicket receipt)
|
||||
{
|
||||
lock (receiptsGate)
|
||||
if (pendingReceipts.TryGetValue(receipt.Key, out var latest) && ReferenceEquals(latest, receipt))
|
||||
pendingReceipts.Remove(receipt.Key);
|
||||
}
|
||||
public void Dispose() { disposed = true; lock (receiptsGate) { pendingReceipts.Clear(); actions.Clear(); } }
|
||||
}
|
||||
|
||||
public sealed class PlayFabPlayerSaveStore : IPlayerSaveStore
|
||||
{
|
||||
readonly PlayFabMgr manager;
|
||||
readonly CancellationToken lifetime;
|
||||
public PlayFabPlayerSaveStore(IUserService user) : this(PlayFabMgr.Instance, user.LoginLifetime) { }
|
||||
internal PlayFabPlayerSaveStore(PlayFabMgr manager, CancellationToken lifetime)
|
||||
{ this.manager = manager; this.lifetime = lifetime; }
|
||||
public bool IsCurrent => manager != null && !lifetime.IsCancellationRequested;
|
||||
|
||||
public PlayerSaveReadResult Read(string key)
|
||||
{
|
||||
if (!IsCurrent) return new PlayerSaveReadResult("StaleOrNotReady");
|
||||
try
|
||||
{
|
||||
var value = manager.GetLocalData(key);
|
||||
return new PlayerSaveReadResult(value != null, value);
|
||||
}
|
||||
catch (Exception ex) { return new PlayerSaveReadResult(ex.GetType().Name); }
|
||||
}
|
||||
|
||||
public PlayerSaveWriteResult Write(string key, string value, bool remoteStore = true)
|
||||
{
|
||||
if (!IsCurrent) return new PlayerSaveWriteResult("StaleOrNotReady");
|
||||
try
|
||||
{
|
||||
manager.UpdateUserDataValue(key, value, remoteStore);
|
||||
return new PlayerSaveWriteResult(new PlayerSaveReceipt(this, remoteStore ? (object)true : null));
|
||||
}
|
||||
catch (Exception ex) { return new PlayerSaveWriteResult(ex.GetType().Name); }
|
||||
}
|
||||
|
||||
public async Task<PlayerSaveConfirmation> WriteAndConfirmAsync(string key, string value, CancellationToken token = default)
|
||||
{
|
||||
if (!IsCurrent || token.IsCancellationRequested) return new PlayerSaveConfirmation(PlayerSaveStatus.Cancelled);
|
||||
using (var wait = CancellationTokenSource.CreateLinkedTokenSource(lifetime, token))
|
||||
{
|
||||
var cancelled = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
using (wait.Token.Register(() => cancelled.TrySetResult(false)))
|
||||
{
|
||||
try
|
||||
{
|
||||
var save = manager.SyncDataImmidiate(key, value);
|
||||
var finished = await Task.WhenAny(save, cancelled.Task);
|
||||
if (!IsCurrent || wait.IsCancellationRequested) return new PlayerSaveConfirmation(PlayerSaveStatus.Cancelled);
|
||||
return new PlayerSaveConfirmation(await finished ? PlayerSaveStatus.Confirmed : PlayerSaveStatus.Rejected);
|
||||
}
|
||||
catch (Exception ex) { return new PlayerSaveConfirmation(PlayerSaveStatus.Rejected, ex.GetType().Name); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task<PlayerSaveConfirmation> ConfirmAsync(PlayerSaveReceipt receipt, CancellationToken token = default) =>
|
||||
Task.FromResult(new PlayerSaveConfirmation(!IsCurrent || token.IsCancellationRequested ? PlayerSaveStatus.Cancelled :
|
||||
receipt == null || !ReferenceEquals(receipt.Store, this) ? PlayerSaveStatus.Rejected :
|
||||
receipt.Ticket == null ? PlayerSaveStatus.LocalOnly : PlayerSaveStatus.Buffered));
|
||||
|
||||
public Task<PlayerSaveConfirmation> FlushAsync(CancellationToken token = default)
|
||||
{
|
||||
if (!IsCurrent || token.IsCancellationRequested)
|
||||
return Task.FromResult(new PlayerSaveConfirmation(PlayerSaveStatus.Cancelled));
|
||||
// The original manager owns its timer and callback-based buffered dispatch.
|
||||
manager.FlushBufferedData();
|
||||
return Task.FromResult(new PlayerSaveConfirmation(PlayerSaveStatus.Buffered));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using game.PlayerDataStorage;
|
||||
using Newtonsoft.Json;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
public class SurveyService
|
||||
{
|
||||
const string SurveyUrl = "https://wesurvey.com/";
|
||||
const string SurveyData = "SurveyData";
|
||||
const string SurveyReadSidData = "SurveyReadSidData";
|
||||
|
||||
readonly IPlayerSaveStore saveStore;
|
||||
readonly HashSet<string> pendingRewards = new HashSet<string>();
|
||||
IUserService userService;
|
||||
cfg.Tables tables;
|
||||
List<string> receivedReward = new List<string>();
|
||||
List<string> readSid = new List<string>();
|
||||
public SurveyData tableData;
|
||||
public SurveyService(IUserService userService, cfg.Tables tables, IPlayerSaveStore saveStore)
|
||||
{
|
||||
this.userService = userService;
|
||||
this.tables = tables;
|
||||
tableData = null;
|
||||
this.saveStore = saveStore;
|
||||
receivedReward = ReadList(SurveyData);
|
||||
readSid = ReadList(SurveyReadSidData);
|
||||
}
|
||||
List<string> ReadList(string key)
|
||||
{
|
||||
var read = saveStore.Read(key);
|
||||
var json = read.ValueOrThrow();
|
||||
if (read.Found == false) return new List<string>();
|
||||
if (string.IsNullOrEmpty(json)) throw new JsonSerializationException("Empty survey save: " + key);
|
||||
return JsonConvert.DeserializeObject<List<string>>(json) ?? throw new JsonSerializationException("Null survey save: " + key);
|
||||
}
|
||||
public bool IsCurrent => saveStore.IsCurrent && !userService.LoginLifetime.IsCancellationRequested;
|
||||
/// <summary>
|
||||
/// 拉取问卷
|
||||
/// </summary>
|
||||
public void GetSurveyMatchlist()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsCurrent) return;
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
|
||||
List<SurveyData> surveyDataTable = tables.TbSurveyData.DataList;
|
||||
ulong userId = Convert.ToUInt64(userService.UserId, saveStore is KVPlayerSaveStore ? 10 : 16);
|
||||
tableData = null;
|
||||
if (surveyDataTable.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < surveyDataTable.Count; i++)
|
||||
{
|
||||
if (!receivedReward.Contains(surveyDataTable[i].ID.ToString()))
|
||||
{
|
||||
if (surveyDataTable[i].LevelCondition >= playerData.lv)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int tailNumber = (int)(userId % 10);
|
||||
if (!surveyDataTable[i].TailNumberList.Contains(tailNumber))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tableData = surveyDataTable[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
////设置红点
|
||||
SetRedPoint();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError("GetSurveyMatchlist:" + e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetRedPoint()
|
||||
{
|
||||
if (tableData == null)
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState("menu.survey", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
RedPointManager.Instance.SetRedPointState("menu.survey", true);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 请求答题的链接
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetSurveyAnswer()
|
||||
{
|
||||
if (!IsCurrent || tableData == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
string url = $"{SurveyUrl}s2/{tableData.ID}/{tableData.Hash}/?&uid={userService.CustomId}";
|
||||
//标记已读
|
||||
var next = new List<string>(readSid);
|
||||
if (!next.Contains(tableData.ID.ToString())) next.Add(tableData.ID.ToString());
|
||||
saveStore.Write(SurveyReadSidData, JsonConvert.SerializeObject(next)).RequireAccepted();
|
||||
readSid = next;
|
||||
GetSurveyMatchlist();
|
||||
return url;
|
||||
}
|
||||
/// <summary>
|
||||
/// 检查是否能领奖
|
||||
/// </summary>
|
||||
public async Task GetSurveyCheckAnswer(SurveyData survey)
|
||||
{
|
||||
if (!IsCurrent) throw new OperationCanceledException();
|
||||
if (survey == null) return;
|
||||
string sid = survey.ID.ToString();
|
||||
if (pendingRewards.Contains(sid)) throw new InvalidOperationException("Survey reward is pending or requires recovery.");
|
||||
if (receivedReward.Contains(sid)) return;
|
||||
pendingRewards.Add(sid);
|
||||
var items = GContext.container.Resolve<PlayerItemData>();
|
||||
var player = GContext.container.Resolve<PlayerData>();
|
||||
await PlayerSaveAction.RunAsync(saveStore, "survey:" + sid, () =>
|
||||
{
|
||||
items.AddItemByDrop(survey.Reward, null);
|
||||
PlayerSaveAction.Present(() => GContext.Publish(new ShowData()));
|
||||
receivedReward.Add(sid);
|
||||
saveStore.Write(SurveyData, JsonConvert.SerializeObject(receivedReward)).RequireAccepted();
|
||||
}, () => player.SavePlayerData(), userService.LoginLifetime);
|
||||
if (!IsCurrent) throw new OperationCanceledException();
|
||||
pendingRewards.Remove(sid);
|
||||
GetSurveyMatchlist();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,557 @@
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
internal static partial class PlayerKVGameplayLoop
|
||||
{
|
||||
internal static async Task<string> VerifyFishingRewardForEditorAsync()
|
||||
{
|
||||
if(UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode || GContext.Ready ||
|
||||
Resources.FindObjectsOfTypeAll<GContext>().Length!=0 || PlayFabMgr.HasLiveInstanceForKVLoad || Enabled || handle!=null ||
|
||||
File.Exists(Path.Combine(Root,"permit.json")) || validationCleanupFailure!=null)
|
||||
throw new InvalidOperationException("Idle editor without a real permit required.");
|
||||
if(Interlocked.CompareExchange(ref validationRunning,1,0)!=0) throw new InvalidOperationException("Validation running.");
|
||||
var saved=(Enabled,Ready,Error,Permit,RunPath,handle,onFault,onClose,collecting,mainThread,failed,mainContext,fishingAction,rewardPlan,rewardAction);
|
||||
var oldContainer=GContext.container;var oldEvents=FishingContextAccess.Events;
|
||||
var folder=Path.Combine(Root,"reward-fixed-runs",DateTime.UtcNow.ToString("yyyyMMddTHHmmss")+"_"+Guid.NewGuid().ToString("N"));
|
||||
EquipmentFixture fixture=null;Task pending=null;Container container=null;PlayerData player=null;
|
||||
RewardSubscribers subscribers=null;
|
||||
var oldLanguages=LocalizationMgr.enumLanguage;
|
||||
var textTable=typeof(LocalizationMgr).GetField("table",BindingFlags.Static|BindingFlags.NonPublic);
|
||||
var oldTextTable=textTable.GetValue(null);
|
||||
GameObject pfShell=null;var pfField=typeof(PlayFabMgr).GetField("_instance",BindingFlags.Static|BindingFlags.NonPublic);var oldPF=pfField.GetValue(null);
|
||||
int checks=0,groups=0;var passed=new List<string>();string result=null;
|
||||
var ioDiagnostics=new System.Collections.Concurrent.ConcurrentQueue<string>();
|
||||
EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs> observeIO=(_,e)=>
|
||||
{if(e.Exception is IOException || e.Exception is UnauthorizedAccessException)ioDiagnostics.Enqueue(e.Exception.ToString());};
|
||||
AppDomain.CurrentDomain.FirstChanceException+=observeIO;
|
||||
void Check(bool yes,string reason) {checks++;if(!yes)throw new InvalidOperationException("Reward validation: "+reason);}
|
||||
async Task Start(string name)
|
||||
{
|
||||
subscribers?.Dispose();subscribers=null;
|
||||
player?.StopRecoveryEnergy();player=null;
|
||||
if(fixture!=null)await fixture.Close();if(pending!=null){try{await pending;}catch{}pending=null;}
|
||||
pfField.SetValue(null,oldPF);if(pfShell!=null)UnityEngine.Object.DestroyImmediate(pfShell);pfShell=null;
|
||||
FishingContextAccess.Use(oldContainer);FishingContextAccess.Events=oldEvents;container?.Dispose();container=null;
|
||||
fixture=new EquipmentFixture(Path.Combine(folder,(++groups).ToString()));
|
||||
fixture.Remote["Map_5_102"]="{\"10211\":\"0|10|20|20|False\",\"10241\":\"2|20|30|40|False\"}";
|
||||
var owner=await fixture.Open();
|
||||
Enabled=true;Ready=false;Error=null;failed=0;handle=null;collecting=null;
|
||||
Permit=new PlayerKVLoopPermit{Version=3,Operation="fishing-reward-once",PlayerId="101",
|
||||
AllowedKeys=new[]{"NewPlayerDataDic","Map_5_102","BattlePassRecordTask","late"}};
|
||||
RunPath=folder;mainThread=Thread.CurrentThread.ManagedThreadId;mainContext=SynchronizationContext.Current;
|
||||
Attach(_=>fixture.Faults++,fixture.Close);Publish(owner);
|
||||
fishingAction=new FishingAction(new object(),owner){Consuming=true,Confirmed=true,Continued=true};
|
||||
rewardPlan=new FishingRewardPlan{Fish=10211,Area=102};
|
||||
rewardAction=new FishingRewardAction(fishingAction,new object(),rewardPlan);
|
||||
passed.Add(name);
|
||||
}
|
||||
PlayerKVEditResult Batch(bool localFinal=false)
|
||||
{
|
||||
using(BeginRewardSegment("strike")) Write("BattlePassRecordTask","strike",true);
|
||||
rewardAction.Ending=true;using(BeginRewardSegment("ending")){}
|
||||
rewardAction.Applying=true;
|
||||
PlayerKVEditResult primary;
|
||||
using(BeginRewardSegment("reward"))
|
||||
{
|
||||
Write("BattlePassRecordTask","reward-task",true);
|
||||
Write("Map_5_102","fish",!localFinal);
|
||||
Write("NewPlayerDataDic","intermediate",false);
|
||||
primary=Write("NewPlayerDataDic","reward-main",true);
|
||||
}
|
||||
rewardAction.GuideNotifications=1;
|
||||
return primary;
|
||||
}
|
||||
async Task Reject(Task task,string label)
|
||||
{
|
||||
bool rejected=false;try{await task;}catch(InvalidOperationException){rejected=true;}
|
||||
Check(rejected && !Ready && fixture.Faults==1,label);
|
||||
}
|
||||
void RejectSync(Action body,string label)
|
||||
{bool rejected=false;try{body();}catch(InvalidOperationException){rejected=true;}Check(rejected,label);}
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
// Exercise the real async Init path, using isolated services and the actual loaded guide asset.
|
||||
var potionSource=JObject.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,
|
||||
"../../CodexOutput/upserver/KVGameplayLoopTests/backups/20260928T082052Z/101.json"))))["data"].ToObject<Dictionary<string,string>>();
|
||||
var guideAssetHandle=UnityEngine.AddressableAssets.Addressables.LoadAssetAsync<object>("GuidanceConfig");
|
||||
var guideAsset=(GuidanceDefineSetting)guideAssetHandle.WaitForCompletion();
|
||||
var savedGuideGroups=guideAsset.guidanceGroupDefines;
|
||||
var awaitAssembly=typeof(IEnumeratorAwaitExtensions).Assembly;
|
||||
var syncType=awaitAssembly.GetType("UnityAsyncAwaitUtil.SyncContextUtil",true);
|
||||
var syncField=syncType.GetField("<UnitySynchronizationContext>k__BackingField",BindingFlags.Static|BindingFlags.NonPublic);
|
||||
var threadField=syncType.GetField("<UnityThreadId>k__BackingField",BindingFlags.Static|BindingFlags.NonPublic);
|
||||
var runnerField=awaitAssembly.GetType("UnityAsyncAwaitUtil.AsyncCoroutineRunner",true).GetField("_instance",BindingFlags.Static|BindingFlags.NonPublic);
|
||||
var savedSync=syncField.GetValue(null);var savedThread=threadField.GetValue(null);var savedRunner=runnerField.GetValue(null);
|
||||
syncField.SetValue(null,SynchronizationContext.Current);threadField.SetValue(null,Thread.CurrentThread.ManagedThreadId);
|
||||
try
|
||||
{
|
||||
foreach(var scenario in new[]{"unavailable-real-101","available","query-failure","node-panel-mismatch",
|
||||
"other-group","finished","index-zero","missing-save","out-of-range","preserve-other-pointer"})
|
||||
{
|
||||
await Start("potion-actual-init-"+scenario);
|
||||
rewardPlan=null;rewardAction=null;
|
||||
Permit.AllowedKeys=Permit.AllowedKeys.Concat(new[]{"GuideSaveDataNew"}).ToArray();
|
||||
var tables=new cfg.Tables(n=>SimpleJSON.JSON.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../cfgData",n+".json")))));
|
||||
container=new Container();FishingContextAccess.Use(container);FishingContextAccess.Events=new EventAggregator();
|
||||
player=new PlayerData(tables,null);player.lv=62;
|
||||
container.RegisterInstance(tables);container.RegisterInstance(player);
|
||||
var eventData=new FishingEventData(tables);
|
||||
if(scenario!="query-failure")container.RegisterInstance(eventData);
|
||||
if(scenario=="available")
|
||||
{
|
||||
var ev=tables.TbFishingEvent.DataList.First(e=>e.Type==4 && e.SubType==5);
|
||||
typeof(cfg.FishingEvent).GetProperty("TimeDefinition").SetValue(ev,new cfg.LimitedTime(
|
||||
ZZTimeHelper.UtcNow().AddHours(-1).ToString("yyyy/MM/dd HH:mm:ss",System.Globalization.CultureInfo.InvariantCulture),
|
||||
ZZTimeHelper.UtcNow().AddHours(1).ToString("yyyy/MM/dd HH:mm:ss",System.Globalization.CultureInfo.InvariantCulture)));
|
||||
typeof(cfg.FishingEvent).GetProperty("ConditionList").SetValue(ev,new List<cfg.EventCondition>{
|
||||
new cfg.EventCondition(cfg.ConditionType.AccountLevel,new List<string>{"1"},"")});
|
||||
Check(eventData.GetEvent(4,5)==ev.ID,"isolated actual query sees available potion event");
|
||||
}
|
||||
else if(scenario!="query-failure")Check(eventData.GetEvent(4,5)==-1,"isolated actual query sees no potion event");
|
||||
var guideState=new GuideDataCenter(tables,player);container.RegisterInstance(guideState);
|
||||
var diagnostics=new PlayerKVSchemaDiagnostics();guideState.ReplaceFromKVDecoded(GuideDataCenter.DecodeForKVLoad(potionSource,diagnostics));
|
||||
Check(diagnostics.TotalErrors==0,"real 101 guide source decoded");
|
||||
var potionGroup=JsonUtility.FromJson<GuidanceGroupDefine>(JsonUtility.ToJson(savedGuideGroups.First(g=>g.groupName==GroupName.EventPotion01)));
|
||||
guideAsset.guidanceGroupDefines=savedGuideGroups.Select(g=>g.groupName==GroupName.EventPotion01?potionGroup:g).ToArray();
|
||||
var key=GroupName.EventPotion01.ToString();var entry=guideState.GuideSaveDataDic[key];
|
||||
Check(entry.guideSubIndex==4 && !entry.isFinish,"real historical potion progress is unfinished at four");
|
||||
if(scenario=="node-panel-mismatch")potionGroup.guidanceDefines[entry.guideSubIndex].panelName="DifferentPanel";
|
||||
if(scenario=="other-group")
|
||||
{
|
||||
guideState.GuideSaveDataDic.Remove(key);key=GroupName.EventPotion02.ToString();
|
||||
entry.groupName=key;guideState.GuideSaveDataDic[key]=entry;
|
||||
guideAsset.guidanceGroupDefines=guideAsset.guidanceGroupDefines.Where(g=>g.groupName!=GroupName.EventPotion02).ToArray();
|
||||
potionGroup.groupName=GroupName.EventPotion02;
|
||||
}
|
||||
if(scenario=="finished")entry.isFinish=true;
|
||||
if(scenario=="index-zero")entry.guideSubIndex=0;
|
||||
if(scenario=="missing-save")guideState.GuideSaveDataDic.Remove(key);
|
||||
if(scenario=="out-of-range")entry.guideSubIndex=potionGroup.guidanceDefines.Length;
|
||||
var otherGroup=savedGuideGroups.First(g=>g.groupName==GroupName.Fish_01);
|
||||
if(scenario=="preserve-other-pointer")
|
||||
{
|
||||
guideState.curGroupDefine=otherGroup;guideState.curDefine=otherGroup.guidanceDefines[0];
|
||||
guideState.curSaveData=guideState.GuideSaveDataDic[otherGroup.groupName.ToString()];
|
||||
}
|
||||
var before=JsonConvert.SerializeObject(guideState.GuideSaveDataDic);
|
||||
pfShell=new GameObject("KV inactive guide Init fixture");pfShell.SetActive(false);pfShell.hideFlags=HideFlags.HideAndDontSave;
|
||||
var pf=pfShell.AddComponent<PlayFabMgr>();
|
||||
Check(pf.IsUnstartedForKVLoad,"guide fixture never starts PF lifecycle");pfField.SetValue(null,pf);
|
||||
var initErrors=new List<string>();var initWarnings=new List<string>();int completeEvents=0;
|
||||
Application.LogCallback onLog=(message,stack,type)=>{if(type==LogType.Exception || type==LogType.Error)initErrors.Add(message);if(type==LogType.Warning)initWarnings.Add(message);};
|
||||
Application.logMessageReceived+=onLog;
|
||||
var completion=GContext.OnEvent<ConditionTypeEvent>().Subscribe(e=>{if(e.type==cfg.ConditionType.CompleteGuide)completeEvents++;});
|
||||
bool initStarted=false;
|
||||
try
|
||||
{
|
||||
// Asset is preloaded above: Init has no outstanding asset operation when teardown begins.
|
||||
initStarted=true;guideState.Init();
|
||||
var deadline=System.Diagnostics.Stopwatch.StartNew();
|
||||
while(guideState.guidanceGroupDefines==null && deadline.ElapsedMilliseconds<10000)await Task.Delay(10);
|
||||
await Task.Delay(1);
|
||||
Check(guideState.guidanceGroupDefines!=null && initErrors.Count==0,"actual Init completed without asynchronous errors: "+scenario+"; "+string.Join(" | ",initErrors));
|
||||
if(scenario=="unavailable-real-101")
|
||||
{
|
||||
Check(guideState.curDefine==null && guideState.curGroupDefine==null && guideState.curSaveData==null,
|
||||
"unavailable historical potion guide is not activated by actual Init");
|
||||
Check(before==JsonConvert.SerializeObject(guideState.GuideSaveDataDic),"deferred actual Init preserves every guide entry");
|
||||
guideState.AddIndex(true);GContext.Publish(new GuideNextEvent());
|
||||
Check(before==JsonConvert.SerializeObject(guideState.GuideSaveDataDic) && completeEvents==0,
|
||||
"real AddIndex and GuideNext cannot finish deferred potion progress");
|
||||
Check(!handle.ReadLocalSnapshot().Data.ContainsKey("GuideSaveDataNew") && fixture.Sets==0,
|
||||
"deferral and callbacks introduce no guide write or remote request");
|
||||
}
|
||||
else if(scenario=="preserve-other-pointer")
|
||||
Check(ReferenceEquals(guideState.curGroupDefine,otherGroup) && ReferenceEquals(guideState.curDefine,otherGroup.guidanceDefines[0]),"deferral does not clear another active guide");
|
||||
else if(scenario=="available" || scenario=="query-failure" || scenario=="node-panel-mismatch" || scenario=="other-group")
|
||||
Check(ReferenceEquals(guideState.curGroupDefine,potionGroup) && guideState.curDefine!=null,"non-deferred input retains original recovery: "+scenario);
|
||||
else
|
||||
Check(guideState.curGroupDefine==null && guideState.curDefine==null,"original non-resuming boundary retained: "+scenario);
|
||||
if(scenario!="missing-save")Check(ReferenceEquals(guideState.GuideSaveDataDic[key],entry),"saved progress object retained: "+scenario);
|
||||
if(scenario=="query-failure")Check(initWarnings.Any(w=>w.Contains("Potion guide availability")),"query failure is reported and not treated as unavailable");
|
||||
}
|
||||
finally
|
||||
{
|
||||
completion.Dispose();Application.logMessageReceived-=onLog;guideState.Dispose();
|
||||
guideAsset.guidanceGroupDefines=savedGuideGroups;
|
||||
if(initStarted)UnityEngine.AddressableAssets.Addressables.Release(guideAsset);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
guideAsset.guidanceGroupDefines=savedGuideGroups;
|
||||
UnityEngine.AddressableAssets.Addressables.Release(guideAssetHandle);
|
||||
var runner=runnerField.GetValue(null) as MonoBehaviour;
|
||||
if(runner!=null && !ReferenceEquals(runner,savedRunner))UnityEngine.Object.DestroyImmediate(runner.gameObject);
|
||||
runnerField.SetValue(null,savedRunner);syncField.SetValue(null,savedSync);threadField.SetValue(null,savedThread);
|
||||
}
|
||||
await Start("original-tickets-cross-segment-supersession");var primary=Batch();
|
||||
Check(collecting==null && rewardAction.Segment==null && rewardAction.Records.Count==5,"three scopes disposed and all original writes captured");
|
||||
await ConfirmRewardAsync(rewardAction,primary);int shows=0;PresentReward(rewardAction,()=>shows++);PresentReward(rewardAction,()=>shows++);
|
||||
Check(shows==1 && fixture.Remote["Map_5_102"]=="fish" && fixture.Remote["BattlePassRecordTask"]=="reward-task","final values and one presentation");
|
||||
|
||||
await Start("pending-cannot-present");primary=Batch();fixture.HoldSet=true;
|
||||
pending=ConfirmRewardAsync(rewardAction,primary);await fixture.SetEntered.Task;
|
||||
Check(!rewardAction.Confirmed && !rewardAction.Presented,"pending is not success");
|
||||
fixture.ReleaseSet.TrySetResult(true);await pending;pending=null;Check(rewardAction.Confirmed,"response confirms original action");
|
||||
|
||||
await Start("localOnly-map-final-rejected");await Reject(ConfirmRewardAsync(rewardAction,Batch(true)),"local map not remote confirmed");
|
||||
await Start("outside-segment-before-final-save-rejected");
|
||||
using(BeginRewardSegment("strike"))Write("BattlePassRecordTask","strike",true);
|
||||
RejectSync(()=>Write("NewPlayerDataDic","external",true),"external changed write denied before later reward can hide it");
|
||||
Check(fixture.Sets==0 && ReadRejected(),"stopped facade and no outgoing request");
|
||||
|
||||
await Start("same-value-external-supersession-still-rejected");primary=Batch();
|
||||
Write("NewPlayerDataDic","reward-main",true);
|
||||
await Reject(ConfirmRewardAsync(rewardAction,primary),"same-value external ticket is not the original final ticket");
|
||||
await Start("unexpected-backed-up-key-before-admission");
|
||||
using(BeginRewardSegment("strike"))RejectSync(()=>Write("late","unrelated",true),"backed-up but unrelated reward key rejected");
|
||||
Check(rewardAction.Records.Count==0 && fixture.Sets==0,"no unrelated admission");
|
||||
|
||||
await Start("lost-response-no-reward-replay");primary=Batch();fixture.LoseResponse=true;
|
||||
await Reject(ConfirmRewardAsync(rewardAction,primary),"unknown result stops reward");Check(fixture.Sets==1 && !rewardAction.Presented,"single request only");
|
||||
await Start("unauthorized-no-reauthentication");primary=Batch();fixture.Unauthorized=true;
|
||||
await Reject(ConfirmRewardAsync(rewardAction,primary),"401 stops reward");Check(fixture.Auth==1 && fixture.Sets==1,"no relogin or replay");
|
||||
await Start("partial-commit-is-not-success");primary=Batch();fixture.PartialCommit=true;fixture.LoseResponse=true;
|
||||
await Reject(ConfirmRewardAsync(rewardAction,primary),"partial result remains failure");Check(fixture.Sets==1 && !rewardAction.Confirmed,"no refund or second write");
|
||||
await Start("cancel-after-consume-before-reward-confirmed");primary=Batch();fixture.HoldSet=true;
|
||||
pending=ConfirmRewardAsync(rewardAction,primary);await fixture.SetEntered.Task;
|
||||
CancelFishing(fishingAction,"actual consumed action cancellation");fixture.ReleaseSet.TrySetResult(true);
|
||||
await Reject(pending,"C1 Continued cannot exempt pending reward");pending=null;Check(rewardAction.Cancelled && !rewardAction.Presented,"old callbacks revoked");
|
||||
await Start("confirmed-normal-cancel-no-second-exit");primary=Batch();await ConfirmRewardAsync(rewardAction,primary);
|
||||
CancelReward(rewardAction.Controller,"normal disable");CancelFishing(fishingAction,"normal actor destroy");
|
||||
Check(rewardAction.Cancelled && fishingAction.Cancelled && fixture.Faults==0 && Ready,"confirmed normal disable never faults");
|
||||
await Start("battle-controller-disable-still-cancels");
|
||||
CancelReward(rewardAction.Controller,"controller disabled");
|
||||
Check(rewardAction.Cancelled && !Ready && fixture.Faults==1,"battle controller disable still cancels");
|
||||
await Start("ending-teardown-pre-confirm-not-cancel");primary=Batch();
|
||||
CancelReward(rewardAction.Controller,"controller disabled");CancelReward(rewardAction.Controller,"controller destroyed");
|
||||
Check(!rewardAction.Cancelled && Ready && fixture.Faults==0,"ending teardown window is not a cancellation");
|
||||
await ConfirmRewardAsync(rewardAction,primary);shows=0;PresentReward(rewardAction,()=>shows++);
|
||||
Check(shows==1 && rewardAction.Confirmed && rewardAction.Presented,"reward completes and presents after teardown window");
|
||||
await Start("repeated-segment-and-sealed-action-rejected");
|
||||
using(BeginRewardSegment("strike")){}
|
||||
RejectSync(()=>BeginRewardSegment("strike"),"strike cannot repeat");
|
||||
|
||||
await Start("reward-storage-error-before-send");
|
||||
var file=Directory.GetFiles(Path.Combine(folder,groups.ToString()),"state.json",SearchOption.AllDirectories).Single();
|
||||
using(var locked=new FileStream(file,FileMode.Open,FileAccess.Read,FileShare.None))
|
||||
using(BeginRewardSegment("strike"))RejectSync(()=>Write("NewPlayerDataDic","locked",true),"real state-file storage failure");
|
||||
Check(fixture.Sets==0 && !Ready,"storage fault before network");
|
||||
|
||||
foreach(var scenario in new[]{"non-record","new-record","new-fish"})
|
||||
{
|
||||
await Start("real-fish-final-save-"+scenario);
|
||||
var tables=new cfg.Tables(n=>SimpleJSON.JSON.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../cfgData",n+".json")))));
|
||||
container=new Container();FishingContextAccess.Use(container);FishingContextAccess.Events=new EventAggregator();container.RegisterInstance(tables);
|
||||
player=new PlayerData(tables,null);var fish=new PlayerFishData(tables,null);container.RegisterInstance(player);container.RegisterInstance(fish);
|
||||
var core=new PlayerKVCoreState(new object[]{60,100UL,0,393,5,4,30003,new DateTime(2026,1,1),0,112,7UL,2,102,102,13,102,0,0,0});
|
||||
player.ReplaceFromKVDecoded(new PlayerKVPlayerState(core,new Dictionary<string,string>(),new Dictionary<int,int>{{-1,1}},new List<int>(),0,false,"0"));
|
||||
var data=new Dictionary<int,FishWeightData>{{10241,FishWeightData.Deserialize("2|20|30|40|False")}};
|
||||
if(scenario!="new-fish")data[10211]=FishWeightData.Deserialize("0|10|20|20|False");
|
||||
var fields=BindingFlags.Instance|BindingFlags.NonPublic;
|
||||
typeof(PlayerFishData).GetField("fishData",fields).SetValue(fish,data);
|
||||
typeof(PlayerFishData).GetField("mapFishData",fields).SetValue(fish,new Dictionary<int,Dictionary<int,FishWeightData>>{{102,data}});
|
||||
pfShell=new GameObject("KV reward unstarted PF fixture");pfShell.SetActive(false);pfShell.hideFlags=HideFlags.HideAndDontSave;
|
||||
var pf=pfShell.AddComponent<PlayFabMgr>();Check(pf.IsUnstartedForKVLoad,"PF lifecycle never starts");pfField.SetValue(null,pf);
|
||||
rewardAction.BeforeFishCount=scenario=="new-fish"?0:20;
|
||||
using(BeginRewardSegment("strike")){}rewardAction.Ending=true;using(BeginRewardSegment("ending")){}
|
||||
rewardAction.Applying=true;
|
||||
var events=new List<cfg.ConditionType>();using(GContext.OnEvent<ConditionTypeEvent>().Subscribe(e=>events.Add(e.type)))
|
||||
using(BeginRewardSegment("reward"))
|
||||
{
|
||||
fish.SetGetFishCount(10211);fish.SetWeight(10211,scenario=="new-record"?12:5);
|
||||
fish.SaveCaughtFishForKVLoop(10211);
|
||||
primary=Write("NewPlayerDataDic","fixture-main",true);
|
||||
}
|
||||
rewardAction.GuideNotifications=1;await ConfirmRewardAsync(rewardAction,primary);
|
||||
var savedMap=JsonConvert.DeserializeObject<Dictionary<int,string>>(fixture.Remote["Map_5_102"]);
|
||||
var value=FishWeightData.Deserialize(savedMap[10211]);
|
||||
Check(value.TotalNum==(scenario=="new-fish"?10:30),"original count increment exactly once");
|
||||
Check(value.MaxG==(scenario=="new-record"?12:scenario=="new-fish"?5:10),"original maximum weight rule");
|
||||
Check(savedMap[10241]=="2|20|30|40|False" && value.TotalCard==(scenario=="new-fish"?0:20) && value.Level==0,"other fish and card data unchanged");
|
||||
Check(events.Count==2 && events.Contains(cfg.ConditionType.GetFishWeight) && events.Contains(cfg.ConditionType.GetFishID),"final save never republishes fish events");
|
||||
var fishSchema=new PlayerKVSchemaSnapshot("reward-fish",tables.TbRodData.DataMap.Keys,tables.TbMapData.DataMap.Keys,new Dictionary<string,int>(),System.Globalization.CultureInfo.CurrentCulture,new DateTime(2026,9,24),0,
|
||||
fish:tables.TbFishData.DataMap.ToDictionary(p=>p.Key,p=>new PlayerKVFishConfig(p.Key,p.Value.AreaID,p.Value.Quality,p.Value.FishCardID,p.Value.BaseWeight,p.Value.PointExtraParam,p.Value.WeightRandom)),
|
||||
cards:tables.TbFishCard.DataMap.ToDictionary(p=>p.Key,p=>new PlayerKVFishCardConfig(p.Key,p.Value.MaxLevel,p.Value.CardsRequiredList,p.Value.WeightMagList)));
|
||||
var fishErrors=new PlayerKVSchemaDiagnostics();
|
||||
var decodedFish=PlayerKVSaveSchema.DecodeFish(new Dictionary<string,string>{{"Map_5_102",fixture.Remote["Map_5_102"]}},new Dictionary<string,string>(),fishSchema,fishErrors);
|
||||
Check(decodedFish!=null && fishErrors.TotalErrors==0 && decodedFish.Maps[102][10211].Number==value.TotalNum && decodedFish.RawMaps[102][10211].Weight==value.MaxG,"real Map schema reads the saved catch");
|
||||
}
|
||||
foreach(var cash in new[]{0,122})
|
||||
{
|
||||
await Start("actual-main-reward-and-subscribers-cash-"+cash);
|
||||
var tables=new cfg.Tables(n=>SimpleJSON.JSON.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../cfgData",n+".json")))));
|
||||
container=new Container();FishingContextAccess.Use(container);FishingContextAccess.Events=new EventAggregator();container.RegisterInstance(tables);
|
||||
LocalizationMgr.enumLanguage=tables.TbLanguageConfig.DataList.Select(x=>x.ID).ToList();textTable.SetValue(null,null);
|
||||
player=new PlayerData(tables,null);var fish=new PlayerFishData(tables,null);var small=new SmallGameData(tables,null,null);
|
||||
var achievements=new AchievementDataManager(tables);var box=new FishingBoxData();var events=new FishingEventData(tables);
|
||||
container.RegisterInstance(player);container.RegisterInstance(fish);container.RegisterInstance(small);container.RegisterInstance(achievements);container.RegisterInstance(box);container.RegisterInstance(events);
|
||||
fish.ResetForKVLoad();small.ResetForKVLoad();achievements.ResetForKVLoad();box.ResetForKVLoad();achievements.Init();
|
||||
var core=new PlayerKVCoreState(new object[]{60,100UL,0,393,5,4,30003,new DateTime(2026,1,1),0,112,7UL,2,102,102,13,102,0,0,0});
|
||||
player.ReplaceFromKVDecoded(new PlayerKVPlayerState(core,new Dictionary<string,string>(),new Dictionary<int,int>{{-1,1}},new List<int>(),0,false,"0"));
|
||||
subscribers=new RewardSubscribers();subscribers.Bind(tables,events,container);
|
||||
Permit.AllowedKeys=Permit.AllowedKeys.Concat(rewardTaskKeys).Distinct().ToArray();
|
||||
int countEvents=0;var seen=new List<cfg.ConditionType>();
|
||||
using(GContext.OnEvent<ConditionTypeEvent>().Subscribe(e=>{seen.Add(e.type);if(e.type==cfg.ConditionType.GetFishCount)countEvents++;}))
|
||||
{
|
||||
using(BeginRewardSegment("strike"))GContext.Publish(new ConditionTypeEvent(cfg.ConditionType.PerfectStrikeCount,1));
|
||||
rewardAction.Ending=true;using(BeginRewardSegment("ending")){}
|
||||
rewardAction.Applying=true;
|
||||
using(BeginRewardSegment("reward"))
|
||||
{
|
||||
// Actual unchanged data methods; visual/formula extraction is tested separately.
|
||||
player.SaveFishingFrequency();
|
||||
GContext.Publish(new ConditionTypeEvent(cfg.ConditionType.CashFromFishing,cash));
|
||||
GContext.Publish(new ConditionTypeEvent(cfg.ConditionType.MaxCashFromFishing,cash/10));
|
||||
if(cash>0)player.AddGold((ulong)cash);
|
||||
foreach(var condition in new[]{cfg.ConditionType.GetFishWeight,cfg.ConditionType.GetFishID,cfg.ConditionType.GetBoosFish,cfg.ConditionType.GetBiggerThanHugeFishCount})
|
||||
GContext.Publish(new ConditionTypeEvent(condition,2));
|
||||
Write("Map_5_102",Read("Map_5_102"),true);
|
||||
primary=player.SaveFishingRewardForKVLoop();
|
||||
}
|
||||
}
|
||||
rewardAction.GuideNotifications=1;await ConfirmRewardAsync(rewardAction,primary);
|
||||
var modern=JsonConvert.DeserializeObject<Dictionary<string,string>>(fixture.Remote["NewPlayerDataDic"]);
|
||||
var coreValues=modern["newData"].Split(',');
|
||||
Check(coreValues[1]==(100+cash).ToString() && coreValues[14]=="14" && coreValues[15]=="112","cash and one catch with original x10 total frequency");
|
||||
Check(coreValues[3]=="393" && coreValues[9]=="112" && countEvents==1,"reward never consumes again or repeats count event");
|
||||
Check(modern["ad"]==achievements.AchievementDataToJson() && achievements.GetAchievementDatas(cfg.ConditionType.GetFishCount)==1,"last primary serializes real achievement subscriber");
|
||||
Check(modern.ContainsKey("ld") && modern["ld"].Split('|').Length==3,"original luck serialization preserved");
|
||||
Check(subscribers.Battle.Data.BattleTask.dic_dailyTasks.Any(k=>tables.TbBattlePassTask.GetOrDefault(k.Key).Type==cfg.ConditionType.PerfectStrikeCount && k.Value==1),"S1 real battle task writes");
|
||||
Check(subscribers.Lucky.DicTasks.All(k=>k.Value==0) && subscribers.Magic.DicTasks.All(k=>k.Value==0) && subscribers.Mini.Data.Exp==0,"actual lucky/magic/mini ignore reward conditions absent from their real config");
|
||||
foreach(var condition in rewardConditions.Where(x=>x!=cfg.ConditionType.ConsumeEnergy))
|
||||
Check(seen.Count(x=>x==condition)==1,"reward condition once: "+condition);
|
||||
Check(fixture.Remote.ContainsKey("BattlePassRecordTask") && !fixture.Remote.ContainsKey("MiniBPKeyNew") && !fixture.Remote.ContainsKey("EventLuckGameRecord") && !fixture.Remote.ContainsKey("EventLuckMagicPlayfabData"),"only applicable original task saves confirmed; no invented task updates");
|
||||
var schema=new PlayerKVSchemaSnapshot("reward-main",tables.TbRodData.DataMap.Keys,tables.TbMapData.DataMap.Keys,new Dictionary<string,int>(),System.Globalization.CultureInfo.CurrentCulture,new DateTime(2026,9,24),0);
|
||||
var errors=new PlayerKVSchemaDiagnostics();Check(PlayerKVSaveSchema.DecodeCore(new Dictionary<string,string>(),modern,schema,errors)!=null && errors.TotalErrors==0,"main schema reads final original serializer");
|
||||
}
|
||||
await Start("actual-reentry-and-old-permit-gates");
|
||||
Check(!BeginRewardDrawing(rewardAction.Controller),"duplicate drawing never reaches original battle");
|
||||
rewardAction.Ending=true;Check(!BeginRewardEnding(rewardAction.Controller,true),"duplicate fish end never opens another panel");
|
||||
rewardAction.Applying=true;Check(BindRewardPanel(new object())==null,"second panel never reapplies rewards");
|
||||
Permit.Version=2;Permit.Operation="fishing-consume-once";RejectSync(()=>BeginRewardDrawing(new object()),"v2 cannot draw");
|
||||
await Start("v1-cannot-draw");Permit.Version=1;Permit.Operation=null;RejectSync(()=>BeginRewardDrawing(new object()),"v1 cannot draw");
|
||||
await Start("natural-failure-stops-without-reward");
|
||||
RejectSync(()=>BeginRewardEnding(rewardAction.Controller,false),"natural failure not converted to catch");
|
||||
Check(fixture.Sets==0 && rewardAction.Records.Count==0 && !rewardAction.Applying,"failure has no reward or refund");
|
||||
foreach(bool complete in new[]{false,true})
|
||||
{
|
||||
await Start(complete?"actual-panel-disable-confirmed":"actual-panel-disable-pending");
|
||||
primary=Batch();if(complete)await ConfirmRewardAsync(rewardAction,primary);
|
||||
var shell=new GameObject("KV inactive reward panel lifecycle fixture");shell.SetActive(false);shell.hideFlags=HideFlags.HideAndDontSave;
|
||||
try
|
||||
{
|
||||
var panel=shell.AddComponent<FishingRewardPanel>();rewardAction.Panel=panel;
|
||||
typeof(FishingRewardPanel).GetMethod("OnDisable",BindingFlags.Instance|BindingFlags.NonPublic).Invoke(panel,null);
|
||||
Check(rewardAction.Cancelled && fixture.Faults==(complete?0:1),"actual panel OnDisable revokes pending only");
|
||||
Check(!RewardPanelCurrent(rewardAction,panel),"old panel continuation is revoked");
|
||||
}
|
||||
finally{UnityEngine.Object.DestroyImmediate(shell);}
|
||||
}
|
||||
await Start("real-backup-no-special-fish-guide-preflight");
|
||||
container=new Container();FishingContextAccess.Use(container);FishingContextAccess.Events=new EventAggregator();
|
||||
var guide=new GuideDataCenter(null,null);container.RegisterInstance(guide);
|
||||
var backup=JsonConvert.DeserializeObject<Dictionary<string,object>>(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../CodexOutput/upserver/KVGameplayLoopTests/backups/20260924T084836Z/102.json"))));
|
||||
var raw=JsonConvert.DeserializeObject<Dictionary<string,string>>(backup["data"].ToString());
|
||||
var guideErrors=new PlayerKVSchemaDiagnostics();guide.ReplaceFromKVDecoded(GuideDataCenter.DecodeForKVLoad(raw,guideErrors));
|
||||
guide.guidanceGroupDefines=UnityEditor.AssetDatabase.LoadAssetAtPath<GuidanceDefineSetting>("Assets/ABPackage/pkgPrepare/Export/GuidanceConfig.asset").guidanceGroupDefines;
|
||||
Check(guideErrors.TotalErrors==0,"real guide backup decodes");
|
||||
var gateTables=new cfg.Tables(n=>SimpleJSON.JSON.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../cfgData",n+".json")))));
|
||||
container.RegisterInstance(gateTables);
|
||||
rewardPlan.Data=new FishingData{curFishData=gateTables.TbFishData.GetOrDefault(10211)};
|
||||
var guideBefore=JsonConvert.SerializeObject(guide.GuideSaveDataDic);
|
||||
CheckRewardGuide();
|
||||
Check(Ready && fixture.Sets==0 && guideBefore==JsonConvert.SerializeObject(guide.GuideSaveDataDic),"ordinary fish admitted without guide writes or consumption");
|
||||
var originalGroups=guide.guidanceGroupDefines;
|
||||
foreach(var kind in new[]{"ordinary-incomplete","active-node","active-group","last-node","extra-node","missing-node","trigger","parameter","unknown-group","positive-skill","last-skill","escape-skill","active-skill","active-dash","active-dirty"})
|
||||
{
|
||||
// Read-only fault probes share an isolated fixture; no real run is reset.
|
||||
var guideCopy=JsonUtility.FromJson<GuidanceGroupDefine>(JsonUtility.ToJson(originalGroups.First(g=>g.groupName.ToString()=="FishSkill01")));
|
||||
var baseFish=gateTables.TbFishData.GetOrDefault(10211);
|
||||
var fishJson=SimpleJSON.JSON.Parse(baseFish.json.ToString());
|
||||
var testFish=new cfg.FishData(fishJson);
|
||||
var testData=new FishingData{curFishData=testFish};
|
||||
guide.guidanceGroupDefines=originalGroups.Select(g=>g.groupName==guideCopy.groupName?guideCopy:g).ToArray();
|
||||
guide.curDefine=null;guide.curGroupDefine=null;
|
||||
guide.GuideSaveDataDic["Fish_01"].isFinish=true;
|
||||
switch(kind)
|
||||
{
|
||||
case "ordinary-incomplete":guide.GuideSaveDataDic["Fish_01"].isFinish=false;break;
|
||||
case "active-node":guide.curDefine=guideCopy.guidanceDefines[0];break;
|
||||
case "active-group":guide.curGroupDefine=guideCopy;break;
|
||||
case "last-node":guideCopy.guidanceDefines.Last().panelName="FishingRewardPanel";break;
|
||||
case "extra-node":guideCopy.guidanceDefines=guideCopy.guidanceDefines.Concat(new[]{new GuidanceDefine{panelName="FishingPanel_Advanced"}}).ToArray();break;
|
||||
case "missing-node":guideCopy.guidanceDefines=Array.Empty<GuidanceDefine>();break;
|
||||
case "trigger":guideCopy.TriggerConditionsType=GuideTriggerConditions.Next;break;
|
||||
case "parameter":guideCopy.TriggerConditionsParameter="changed";break;
|
||||
case "unknown-group":guideCopy.groupName=(GroupName)999999;break;
|
||||
case "positive-skill":case "last-skill":case "escape-skill":
|
||||
var skillJson=SimpleJSON.JSON.Parse(baseFish.FishEscape.json.ToString());skillJson["SpSkillID"]=123456789;
|
||||
if(kind=="escape-skill")fishJson["FishEscape"]=skillJson;
|
||||
else {var list=new SimpleJSON.JSONArray();if(kind=="last-skill")list.Add(SimpleJSON.JSON.Parse(baseFish.FishEscape.json.ToString()));list.Add(skillJson);fishJson["FishSKillList"]=list;}
|
||||
testData.curFishData=new cfg.FishData(fishJson);break;
|
||||
case "active-skill":testData.fishSpSkillData=new CorruptSpSkillData();break;
|
||||
case "active-dash":testData.superDashSpSkillData=new SuperDashSpSkillData();break;
|
||||
case "active-dirty":testData.dirtyStuckSpSkillData=new DirtyStuckSpSkillData();break;
|
||||
}
|
||||
// Stop() is idempotent; all cases are read-only gate probes, not fresh consumption attempts.
|
||||
RejectSync(()=>CheckRewardGuide(testData),"guide/skill drift denied: "+kind);
|
||||
Check(fixture.Sets==0,"gate drift never writes: "+kind);
|
||||
}
|
||||
guide.guidanceGroupDefines=originalGroups;guide.curDefine=null;guide.curGroupDefine=null;guide.GuideSaveDataDic["Fish_01"].isFinish=true;
|
||||
|
||||
foreach(var scenario in new[]{"ordinary","zero-cruise","cruise","invalid-cruise","free","retry","fixed","duel","special-item","missing-map","wrong-area","event-field","event-count","trigger-pack","activity-drift","guide-before-S1","guide-before-S2","guide-before-S3"})
|
||||
{
|
||||
await Start("actual-full-reward-preflight-"+scenario);
|
||||
var tables=new cfg.Tables(n=>SimpleJSON.JSON.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../cfgData",n+".json")))));
|
||||
container=new Container();FishingContextAccess.Use(container);FishingContextAccess.Events=new EventAggregator();container.RegisterInstance(tables);
|
||||
player=new PlayerData(tables,null);var fish=new PlayerFishData(tables,null);fish.ResetForKVLoad();
|
||||
var events=new FishingEventData(tables);var guideState=new GuideDataCenter(tables,player);
|
||||
var diag=new PlayerKVSchemaDiagnostics();guideState.ReplaceFromKVDecoded(GuideDataCenter.DecodeForKVLoad(raw,diag));guideState.guidanceGroupDefines=originalGroups;
|
||||
guideState.guidanceDefineSetting=UnityEditor.AssetDatabase.LoadAssetAtPath<GuidanceDefineSetting>("Assets/ABPackage/pkgPrepare/Export/GuidanceConfig.asset");
|
||||
var scopes=new Activity.ActivityScopeManager();var manager=new Activity.ActivityManager(tables,scopes);
|
||||
container.RegisterInstance(player);container.RegisterInstance(fish);container.RegisterInstance(events);container.RegisterInstance(guideState);
|
||||
container.RegisterInstance(new CollectingData());container.RegisterInstance(new PiggyBankPackData());container.RegisterInstance(new BargainPackData());container.RegisterInstance(new FishingChallengeManager());
|
||||
container.RegisterInstance(scopes);container.RegisterInstance(manager);
|
||||
player.ReplaceFromKVDecoded(new PlayerKVPlayerState(new PlayerKVCoreState(new object[]{60,100UL,0,393,5,4,30003,new DateTime(2026,1,1),0,112,7UL,2,102,102,13,102,0,0,0}),new Dictionary<string,string>(),new Dictionary<int,int>{{-1,1}},new List<int>(),0,false,"0"));
|
||||
player.IsOpenMagnification=true;fish.IsOpenPiercing=true;
|
||||
var data=new FishingData{curFishData=tables.TbFishData.GetOrDefault(10211),fishItem=tables.TbItem.DataList.First(x=>x.RedirectID==10211 && x.SubType==1),RodId=30003,MapId=102};
|
||||
rewardPlan=null;rewardAction=null;
|
||||
switch(scenario)
|
||||
{
|
||||
case "zero-cruise":case "cruise":case "invalid-cruise":
|
||||
typeof(PlayerFishData).GetField("fishSwimToMap",BindingFlags.Instance|BindingFlags.NonPublic).SetValue(fish,new Dictionary<int,int>{{102,scenario=="zero-cruise"?0:scenario=="cruise"?10211:-2}});break;
|
||||
case "free":data.IsFree=true;break;case "retry":data.IsRetry=true;break;case "duel":data.IsDuel=true;break;
|
||||
case "fixed":data.fixedFishList=tables.TbFixedFishList.DataList.First();break;
|
||||
case "special-item":data.fishItem=tables.TbItem.DataList.First(x=>x.SubType==11);break;
|
||||
case "missing-map":Permit.AllowedKeys=new[]{"NewPlayerDataDic"};break;
|
||||
case "wrong-area":var fj=SimpleJSON.JSON.Parse(data.curFishData.json.ToString());fj["AreaID"]=999999;data.curFishData=new cfg.FishData(fj);break;
|
||||
case "event-field":var ev=tables.TbFishingEvent.DataList.First(x=>x.ID==10100000);typeof(cfg.FishingEvent).GetProperty("PopupPriority").SetValue(ev,ev.PopupPriority+1);break;
|
||||
case "event-count":tables.TbFishingEvent.DataList.Remove(tables.TbFishingEvent.DataList.First(x=>x.ID==10100000));break;
|
||||
case "trigger-pack":typeof(cfg.TriggerPackManager).GetProperty("Condition").SetValue(tables.TbTriggerPackManager.DataList.First(),cfg.ConditionType.GetFishCount);break;
|
||||
}
|
||||
if(scenario=="ordinary" || scenario=="zero-cruise" || scenario=="activity-drift" || scenario.StartsWith("guide-before-"))
|
||||
{
|
||||
PrepareFishingReward(fishingAction,data);
|
||||
Check(Ready && rewardPlan.Data==data && fixture.Sets==0,"actual full preflight admitted before any consumption");
|
||||
if(scenario=="activity-drift")
|
||||
{scopes.CreateActivityScope((1,1));RejectSync(CheckRewardContext,"actual activity scope drift denied");}
|
||||
else if(scenario.StartsWith("guide-before-"))
|
||||
{
|
||||
var actor=new object();
|
||||
if(scenario!="guide-before-S1") {Check(BeginRewardDrawing(actor),"first drawing");using(BeginRewardSegment("strike")){} }
|
||||
if(scenario=="guide-before-S3") {Check(BeginRewardEnding(actor,true),"first ending");using(BeginRewardSegment("ending")){} }
|
||||
guideState.curDefine=originalGroups.First().guidanceDefines.First();
|
||||
if(scenario=="guide-before-S1")RejectSync(()=>BeginRewardDrawing(actor),"active guide before drawing");
|
||||
else if(scenario=="guide-before-S2")RejectSync(()=>BeginRewardEnding(actor,true),"active guide before ending");
|
||||
else RejectSync(()=>BindRewardPanel(new object()),"active guide before reward data");
|
||||
}
|
||||
else
|
||||
{
|
||||
var actor=new object();Check(BeginRewardDrawing(actor),"actual prepare-draw");using(BeginRewardSegment("strike")){}
|
||||
Check(BeginRewardEnding(actor,true),"actual ending");using(BeginRewardSegment("ending")){}
|
||||
Check(BindRewardPanel(new object())!=null,"actual panel binding with before count");
|
||||
using(GContext.OnEvent<OnEventTriggerGuide>().Subscribe(guideState.TriggerGuideNext))
|
||||
using(BeginRewardSegment("reward")){GContext.Publish(new OnEventTriggerGuide("FishingRewardPanel"));RecordRewardGuideNotification();}
|
||||
Check(rewardAction.GuideNotifications==1 && guideState.curDefine==null && guideState.curGroupDefine==null,"one original guide notification and no activated guide");
|
||||
}
|
||||
}
|
||||
else RejectSync(()=>PrepareFishingReward(fishingAction,data),"full gate rejects "+scenario);
|
||||
Check(fixture.Sets==0,"preflight case no network or consumed energy: "+scenario);
|
||||
}
|
||||
await Start("reward-confirmation-timeout-no-replay");primary=Batch();fixture.HoldSet=true;
|
||||
pending=ConfirmRewardAsync(rewardAction,primary);await fixture.SetEntered.Task;
|
||||
await Reject(pending,"actual 30-second timeout");pending=null;fixture.ReleaseSet.TrySetResult(true);
|
||||
Check(fixture.Sets==1 && !rewardAction.Confirmed && !rewardAction.Presented,"timeout never repeats reward");
|
||||
result=JsonConvert.SerializeObject(new{success=true,groups,assertions=checks,passed,formalRequests=0,synthetic=true,folder});
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
subscribers?.Dispose();subscribers=null;
|
||||
LocalizationMgr.enumLanguage=oldLanguages;textTable.SetValue(null,oldTextTable);
|
||||
player?.StopRecoveryEnergy();if(fixture!=null)await fixture.Close();if(pending!=null){try{await pending;}catch{}}
|
||||
pfField.SetValue(null,oldPF);if(pfShell!=null)UnityEngine.Object.DestroyImmediate(pfShell);
|
||||
FishingContextAccess.Use(oldContainer);FishingContextAccess.Events=oldEvents;container?.Dispose();
|
||||
(Enabled,Ready,Error,Permit,RunPath,handle,onFault,onClose,collecting,mainThread,failed,mainContext,fishingAction,rewardPlan,rewardAction)=saved;
|
||||
}
|
||||
catch {validationCleanupFailure=fixture;throw;}
|
||||
finally
|
||||
{
|
||||
AppDomain.CurrentDomain.FirstChanceException-=observeIO;
|
||||
if(Directory.Exists(folder))File.WriteAllText(Path.Combine(folder,"storage-diagnostics.json"),JsonConvert.SerializeObject(ioDiagnostics.ToArray()));
|
||||
Interlocked.Exchange(ref validationRunning,0);
|
||||
}
|
||||
}
|
||||
File.WriteAllText(Path.Combine(folder,"result.json"),result);return result;
|
||||
}
|
||||
sealed class RewardSubscribers:IDisposable
|
||||
{
|
||||
const System.Reflection.BindingFlags Fields=System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic;
|
||||
static readonly System.Reflection.FieldInfo PF=typeof(PlayFabMgr).GetField("_instance",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic);
|
||||
static readonly System.Reflection.FieldInfo Red=typeof(RedPointManager).GetField("instance",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic);
|
||||
readonly object oldRed=Red.GetValue(null),oldPF=PF.GetValue(null);
|
||||
GameObject shell; LuckyTaskData magicTask; MiniBattlePassDataProvider provider;
|
||||
internal BattlePassDataProvider Battle; internal EventLuckyGameModel Lucky;
|
||||
internal EventLuckMagicModel Magic; internal MiniBattlePassDataModel Mini;
|
||||
internal void Bind(cfg.Tables tables,FishingEventData events,Container container)
|
||||
{
|
||||
Red.SetValue(null,new RedPointManager());
|
||||
shell=new GameObject("KV inactive subscriber storage fixture");shell.SetActive(false);shell.hideFlags=HideFlags.HideAndDontSave;
|
||||
var pf=shell.AddComponent<PlayFabMgr>();
|
||||
if(!pf.IsUnstartedForKVLoad)throw new InvalidOperationException("No PF lifecycle may start in this fixture.");
|
||||
PF.SetValue(null,pf);
|
||||
var items=new PlayerItemData(tables);container.RegisterInstance(items);
|
||||
var data=new BattlePassData(tables);
|
||||
data.BattlePassRecord.ID=tables.TbFishingEvent.DataList.First(e=>tables.TbBattlePassMain.GetOrDefault(e.RedirectID)!=null).ID;
|
||||
data.BattlePassRecord.Level=1;
|
||||
foreach(var t in tables.TbBattlePassTask.DataList.Where(t=>rewardConditions.Contains(t.Type)))data.BattleTask.dic_dailyTasks.Add(t.TaskID,0);
|
||||
Battle=new BattlePassDataProvider(tables,data,items);container.RegisterInstance(Battle);
|
||||
var common=tables.TbCommonTask.DataList.Where(t=>rewardConditions.Contains(t.TaskType)).Select(t=>t.Id).ToArray();
|
||||
Lucky=new EventLuckyGameModel(events,tables);foreach(var id in common)Lucky.DicTasks.Add(id,0);
|
||||
Magic=new EventLuckMagicModel{DicTasks=common.ToDictionary(x=>x,x=>0),HigherRingData=EventLuckMagicHigherRingData.CreateEmpty()};
|
||||
var rings=(List<EventLuckMagicRing>)typeof(EventLuckMagicModel).GetField("_rings",Fields).GetValue(Magic);
|
||||
for(int i=1;i<=3;i++)rings.Add(EventLuckMagicRing.CreateEmpty(1,0,(EEventLuckMagicRingType)i));
|
||||
magicTask=new LuckyTaskData(Magic);typeof(EventLuckMagicModel).GetField("_taskData",Fields).SetValue(Magic,magicTask);
|
||||
provider=new MiniBattlePassDataProvider(tables,items);
|
||||
var table=tables.TbMiniBattlePass.DataList.First(t=>t.TaskType==cfg.ConditionType.ConsumeEnergy && t.Param==0);
|
||||
var activity=tables.TbFishingEvent.DataList.First(t=>t.Type==9 && t.RedirectID==table.Id);
|
||||
var type=(MiniBattlePassType)table.Type;
|
||||
Mini=provider.GetModel(type);
|
||||
provider.GetMiniBattlePassRecord("BP_"+type).Id=activity.ID;
|
||||
typeof(MiniBattlePassDataModel).GetField("fishingEvent",Fields).SetValue(Mini,activity);
|
||||
typeof(MiniBattlePassDataModel).GetMethod("InitData",Fields).Invoke(Mini,new object[]{table.Id});
|
||||
// Synthetic fixture has an active window; real-run time is never modified.
|
||||
Mini.startTime=ZZTimeHelper.UtcNow().AddDays(-1);
|
||||
typeof(MiniBattlePassDataModel).GetProperty("endTime").SetValue(Mini,ZZTimeHelper.UtcNow().AddDays(1));
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Battle?.Dispose();Lucky?.Dispose();magicTask?.Dispose();
|
||||
if(Mini!=null)(typeof(MiniBattlePassDataModel).GetField("disposables",Fields).GetValue(Mini) as IDisposable)?.Dispose();
|
||||
PF.SetValue(null,oldPF);Red.SetValue(null,oldRed);
|
||||
if(shell!=null)UnityEngine.Object.DestroyImmediate(shell);
|
||||
}
|
||||
}
|
||||
static bool ReadRejected() {try{Read("NewPlayerDataDic");return false;}catch(InvalidOperationException){return true;}}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,330 @@
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using UniRx;
|
||||
using DG.Tweening;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game.PlayerDataStorage
|
||||
{
|
||||
internal static partial class PlayerKVGameplayLoop
|
||||
{
|
||||
// Only a fixed idle-editor test; the handler inherited from EquipmentFixture never forwards requests.
|
||||
internal static async Task<string> VerifyFishingForEditorAsync()
|
||||
{
|
||||
if(UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode || GContext.Ready ||
|
||||
Resources.FindObjectsOfTypeAll<GContext>().Length!=0 || PlayFabMgr.HasLiveInstanceForKVLoad ||
|
||||
Enabled || handle!=null || File.Exists(Path.Combine(Root,"permit.json")) || validationCleanupFailure!=null)
|
||||
throw new InvalidOperationException("Idle editor without a real permit required.");
|
||||
if(Interlocked.CompareExchange(ref validationRunning,1,0)!=0) throw new InvalidOperationException("Validation running.");
|
||||
var saved=(Enabled,Ready,Error,Permit,RunPath,handle,onFault,onClose,collecting,mainThread,failed,mainContext,fishingAction);
|
||||
var oldContainer=GContext.container; var oldEvents=FishingContextAccess.Events;
|
||||
var folder=Path.Combine(Root,"fishing-fixed-runs",DateTime.UtcNow.ToString("yyyyMMddTHHmmss")+"_"+Guid.NewGuid().ToString("N"));
|
||||
EquipmentFixture fixture=null; Task pending=null; Container container=null; PlayerData player=null; IDisposable subscription=null;
|
||||
var oldLanguages=LocalizationMgr.enumLanguage;
|
||||
var textTable=typeof(LocalizationMgr).GetField("table",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic);
|
||||
var oldTextTable=textTable.GetValue(null);
|
||||
FishingSubscribers subscribers=null;
|
||||
int checks=0,groups=0; var passed=new List<string>(); string result=null;
|
||||
void Check(bool yes,string reason) { checks++; if(!yes) throw new InvalidOperationException("Fishing validation: "+reason); }
|
||||
async Task Start(string name)
|
||||
{
|
||||
subscribers?.Dispose(); subscribers=null;
|
||||
player?.StopRecoveryEnergy(); player=null; subscription?.Dispose(); subscription=null;
|
||||
if(fixture!=null) await fixture.Close();
|
||||
if(pending!=null) { try { await pending; } catch { } pending=null; }
|
||||
FishingContextAccess.Use(oldContainer); FishingContextAccess.Events=oldEvents; container?.Dispose(); container=null;
|
||||
fixture=new EquipmentFixture(Path.Combine(folder,(++groups).ToString()));
|
||||
var owner=await fixture.Open();
|
||||
Enabled=true; Ready=false; Error=null; failed=0; handle=null; collecting=null; fishingAction=null;
|
||||
Permit=new PlayerKVLoopPermit{Version=2,Operation="fishing-consume-once",PlayerId="101",AllowedKeys=new[]{"k","NewPlayerDataDic","late"}};
|
||||
RunPath=folder; mainThread=Thread.CurrentThread.ManagedThreadId; mainContext=SynchronizationContext.Current;
|
||||
Attach(_=>fixture.Faults++,fixture.Close); Publish(owner);
|
||||
passed.Add(name);
|
||||
}
|
||||
(FishingAction action,WriteBatch batch,PlayerKVEditResult primary) Batch(bool duplicates=false)
|
||||
{
|
||||
var action=BeginFishing(new object()); Check(ClaimFishingConsumption(action),"first consume admitted");
|
||||
var batch=BeginFishingWrites(action);
|
||||
if(duplicates) { Write("NewPlayerDataDic","intermediate-local",false); Write("NewPlayerDataDic","intermediate-remote",true); }
|
||||
Write("k","task",true); var primary=Write("NewPlayerDataDic","final",true); batch.Dispose();
|
||||
return(action,batch,primary);
|
||||
}
|
||||
async Task Reject(Task task,string label)
|
||||
{
|
||||
bool rejected=false; try { await task; } catch(InvalidOperationException) { rejected=true; }
|
||||
Check(rejected && !Ready && fixture.Faults==1,label);
|
||||
}
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
await Start("duplicate-state-writes-and-original-final-tickets");
|
||||
var b=Batch(true); Check(b.batch.Records.Count==4 && !b.batch.Records[0].Remote && b.batch.Records[0].Edit.Ticket==null,"local intermediate captured");
|
||||
await ConfirmFishingAsync(b.action,b.batch,b.primary);
|
||||
Check(fixture.Remote["NewPlayerDataDic"]=="final" && fixture.Remote["k"]=="task","both final values persisted");
|
||||
int continued=0; ContinueFishing(b.action,()=>continued++); ContinueFishing(b.action,()=>continued++);
|
||||
Check(continued==1 && !ClaimFishingConsumption(b.action) && BeginFishing(new object())==null,"continue and consume/cast only once");
|
||||
|
||||
await Start("pending-response-does-not-continue"); b=Batch(); fixture.HoldSet=true;
|
||||
pending=ConfirmFishingAsync(b.action,b.batch,b.primary);
|
||||
Check(ReferenceEquals(await Task.WhenAny(fixture.SetEntered.Task,pending,Task.Delay(5000)),fixture.SetEntered.Task),"Set entered");
|
||||
Check(!b.action.Confirmed && !b.action.Continued,"no continuation during pending response");
|
||||
fixture.ReleaseSet.TrySetResult(true); await pending; pending=null;
|
||||
Check(b.action.Confirmed,"original response confirms");
|
||||
|
||||
await Start("final-localOnly-rejected-before-flush"); b=Batch();
|
||||
// A sealed record ledger from actual writes with a localOnly final key.
|
||||
collecting=b.batch; Write("k","local-tail",false); b.batch.Dispose();
|
||||
await Reject(ConfirmFishingAsync(b.action,b.batch,b.primary),"local final cannot report remote success"); Check(fixture.Sets==0,"no flush of rejected batch");
|
||||
|
||||
await Start("external-final-supersession"); b=Batch(); Write("NewPlayerDataDic","later-action",true);
|
||||
await Reject(ConfirmFishingAsync(b.action,b.batch,b.primary),"never substitute later ticket");
|
||||
|
||||
await Start("lost-response-after-commit"); b=Batch(); fixture.LoseResponse=true;
|
||||
await Reject(ConfirmFishingAsync(b.action,b.batch,b.primary),"Unknown stops run");
|
||||
Check(fixture.Sets==1 && fixture.Remote["NewPlayerDataDic"]=="final","one uncertain commit, no replay");
|
||||
|
||||
await Start("unauthorized-does-not-reauthenticate"); b=Batch(); fixture.Unauthorized=true;
|
||||
await Reject(ConfirmFishingAsync(b.action,b.batch,b.primary),"401 stops run"); Check(fixture.Auth==1 && fixture.Sets==1,"single auth and Set");
|
||||
|
||||
await Start("cancelled-object-does-not-continue-after-commit"); b=Batch(); fixture.HoldSet=true;
|
||||
pending=ConfirmFishingAsync(b.action,b.batch,b.primary); await fixture.SetEntered.Task;
|
||||
CancelFishing(b.action,"synthetic disable"); fixture.ReleaseSet.TrySetResult(true);
|
||||
await Reject(pending,"disabled original action cannot continue"); pending=null;
|
||||
Check(!b.action.Continued && b.action.Cancelled,"no stale continuation");
|
||||
|
||||
await Start("close-before-confirmation"); b=Batch(); await fixture.Close();
|
||||
await Reject(ConfirmFishingAsync(b.action,b.batch,b.primary),"closed owner rejected"); Check(fixture.Sets==0,"close does not invent flush");
|
||||
|
||||
await Start("partial-commit-is-not-atomic-success"); b=Batch(); fixture.PartialCommit=true; fixture.LoseResponse=true;
|
||||
await Reject(ConfirmFishingAsync(b.action,b.batch,b.primary),"partial unknown stops run");
|
||||
Check(fixture.Sets==1 && fixture.Remote["k"]=="task" && fixture.Remote["NewPlayerDataDic"]=="original-save","partial commit retained without compensation");
|
||||
|
||||
await Start("storage-error-before-send");
|
||||
var stateFile=Directory.GetFiles(Path.Combine(folder,groups.ToString()),"state.json",SearchOption.AllDirectories).Single();
|
||||
using(var locked=new FileStream(stateFile,FileMode.Open,FileAccess.Read,FileShare.None))
|
||||
{
|
||||
bool storage=false; try { Write("NewPlayerDataDic","locked",true); } catch(InvalidOperationException e) { storage=e.Message.Contains("StorageError"); }
|
||||
Check(storage && !Ready && fixture.Faults==1 && fixture.Sets==0,"storage failure cannot be accepted or retried");
|
||||
}
|
||||
|
||||
foreach(bool complete in new[]{false,true})
|
||||
{
|
||||
await Start(complete?"actual-tween-autoKill-after-complete":"actual-tween-kill-before-complete");
|
||||
var shell=new GameObject("KV inactive fishing tween fixture"); shell.SetActive(false); shell.hideFlags=HideFlags.HideAndDontSave;
|
||||
Tween tween=null;
|
||||
// DOTween.Init deliberately does nothing in EditMode; Kill otherwise returns
|
||||
// before reaching the real tween engine. Scope this fixed fixture gate to this
|
||||
// synchronous block and restore it even when an assertion fails.
|
||||
var initialized=typeof(DOTween).GetField("initialized",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic|System.Reflection.BindingFlags.Public);
|
||||
var wasInitialized=initialized.GetValue(null);
|
||||
try
|
||||
{
|
||||
initialized.SetValue(null,true);
|
||||
var behavior=shell.AddComponent<FishingBehaviorB>();
|
||||
var action=BeginFishing(behavior);
|
||||
typeof(FishingBehaviorB).GetField("kvFishingAction",System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic).SetValue(behavior,action);
|
||||
float value=0; tween=DOTween.To(()=>value,v=>value=v,1,100).SetAutoKill(true).SetUpdate(UpdateType.Manual);
|
||||
typeof(FishingBehaviorB).GetMethod("TrackKVCastTween",System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic).Invoke(behavior,new object[]{tween});
|
||||
Check(tween.onKill!=null && tween.onComplete!=null,"actual callbacks installed");
|
||||
if(complete) tween.Complete(); else tween.Kill(false);
|
||||
Check(action.Cancelled!=complete && !action.Continued,"actual Tween Kill boundary complete="+complete+" cancelled="+action.Cancelled+" enabled="+Enabled+" active="+tween.IsActive());
|
||||
Check(fixture.Faults==(complete?0:1) && fixture.Sets==0,"autoKill is not a cancellation");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { tween?.Kill(false); UnityEngine.Object.DestroyImmediate(shell); }
|
||||
finally { initialized.SetValue(null,wasInitialized); }
|
||||
}
|
||||
}
|
||||
|
||||
await Start("completed-action-normal-disable-is-not-failure");
|
||||
var completedShell=new GameObject("KV completed disable fixture");completedShell.SetActive(false);completedShell.hideFlags=HideFlags.HideAndDontSave;
|
||||
try
|
||||
{
|
||||
var behavior=completedShell.AddComponent<FishingBehaviorB>();
|
||||
var action=BeginFishing(behavior);Check(ClaimFishingConsumption(action),"completed action admitted");
|
||||
var batch=BeginFishingWrites(action);var primary=Write("NewPlayerDataDic","completed",true);batch.Dispose();
|
||||
await ConfirmFishingAsync(action,batch,primary);ContinueFishing(action,()=>{});
|
||||
typeof(FishingBehaviorB).GetField("kvFishingAction",System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic).SetValue(behavior,action);
|
||||
typeof(FishingBehaviorB).GetMethod("OnDisable",System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic).Invoke(behavior,null);
|
||||
Check(action.Cancelled && action.Continued && Ready && fixture.Faults==0,"normal disable after completed consumption must not Fault");
|
||||
Check(!FishingCurrent(action) && BeginFishing(behavior)==null,"normal disable still revokes callbacks and never restores quota");
|
||||
}
|
||||
finally { UnityEngine.Object.DestroyImmediate(completedShell); }
|
||||
|
||||
await Start("observation-timeout-does-not-replay"); b=Batch(); fixture.HoldSet=true;
|
||||
pending=ConfirmFishingAsync(b.action,b.batch,b.primary);
|
||||
await Reject(pending,"30 second observer deadline stops continuation"); pending=null;
|
||||
Check(fixture.Sets==1 && !b.action.Continued,"timeout leaves one admitted write");
|
||||
fixture.ReleaseSet.TrySetResult(true);
|
||||
|
||||
await Start("equipment-permit-cannot-cast"); Permit.Version=1; Permit.Operation=null;
|
||||
bool denied=false; try { BeginFishing(new object()); } catch(InvalidOperationException) { denied=true; }
|
||||
Check(denied && fixture.Sets==0,"old permit cannot consume");
|
||||
|
||||
foreach(var scenario in new[]{"above-limit","cross-limit","below-limit","recovery-during-pending"})
|
||||
{
|
||||
await Start("actual-PlayerData-"+scenario);
|
||||
int initialEnergy=scenario=="above-limit"?400:scenario=="cross-limit"?300:250;
|
||||
var tables=new cfg.Tables(n=>SimpleJSON.JSON.Parse(File.ReadAllText(Path.GetFullPath(Path.Combine(Application.dataPath,"../../cfgData",n+".json")))));
|
||||
container=new Container(); FishingContextAccess.Use(container); FishingContextAccess.Events=new EventAggregator();
|
||||
container.RegisterInstance(tables);
|
||||
LocalizationMgr.enumLanguage=tables.TbLanguageConfig.DataList.Select(x=>x.ID).ToList();
|
||||
textTable.SetValue(null,null);
|
||||
player=new PlayerData(tables,null); var small=new SmallGameData(tables,null,null); var fish=new PlayerFishData(tables,null);
|
||||
var achievements=new AchievementDataManager(tables); var box=new FishingBoxData(); var events=new FishingEventData(tables);
|
||||
container.RegisterInstance(player); container.RegisterInstance(small); container.RegisterInstance(fish);
|
||||
container.RegisterInstance(achievements); container.RegisterInstance(box); container.RegisterInstance(events);
|
||||
container.RegisterInstance<ILocalNotificationService>(new FishingNotifications());
|
||||
fish.ResetForKVLoad(); small.ResetForKVLoad(); achievements.ResetForKVLoad(); box.ResetForKVLoad();
|
||||
achievements.Init();
|
||||
int rod=tables.TbRodData.DataMap.Keys.First(),map=tables.TbMapData.DataMap.Keys.First();
|
||||
var core=new PlayerKVCoreState(new object[]{1,10UL,0,initialEnergy,5,0,rod,new DateTime(2026,1,1),0,7,10UL,2,map,map,0,0,0,0,0});
|
||||
player.ReplaceFromKVDecoded(new PlayerKVPlayerState(core,new Dictionary<string,string>(),new Dictionary<int,int>{{-1,1}},new List<int>(),0,false,"0"));
|
||||
// Fixed backing property only, no caller-controlled reflection or gameplay registration.
|
||||
typeof(PlayerData).GetProperty("benefitsEnergyLimit").SetValue(player,300);
|
||||
typeof(PlayerData).GetProperty("benefitsEnergyRecover").SetValue(player,1);
|
||||
if(scenario=="above-limit")
|
||||
{
|
||||
Permit.AllowedKeys=Permit.AllowedKeys.Concat(new[]{"BattlePassRecordTask","MiniBPKeyNew","EventLuckGameRecord","EventLuckMagicPlayfabData"}).ToArray();
|
||||
subscribers=new FishingSubscribers();
|
||||
subscribers.Bind(tables,events,container);
|
||||
Check(!tables.TbFishingEvent.DataList.Any(e=>e.ConditionList.Any(c=>c.Condition==cfg.ConditionType.ConsumeEnergy)) &&
|
||||
!tables.TbTriggerPackManager.DataList.Any(t=>t.Condition==cfg.ConditionType.ConsumeEnergy),"real config excludes async activation and trigger-pack writes");
|
||||
}
|
||||
int consumed=0,notified=0;
|
||||
subscription=GContext.OnEvent<ConditionTypeEvent>().Subscribe(e=>{if(e.type==cfg.ConditionType.ConsumeEnergy){consumed+=e.count; Write("k","task-"+e.count,true);}});
|
||||
using(GContext.OnEvent<PlayerData>().Subscribe(_=>notified++))
|
||||
{
|
||||
var action=BeginFishing(new object()); Check(ClaimFishingConsumption(action),"real action claimed");
|
||||
fixture.HoldSet=scenario=="recovery-during-pending";
|
||||
var consumption=player.ConsumeEnergyForKVLoopAsync(3,action); pending=consumption;
|
||||
if(fixture.HoldSet)
|
||||
{
|
||||
await fixture.SetEntered.Task;
|
||||
Check(!action.Confirmed && !action.Continued,"real player pending cannot continue");
|
||||
// Drive the actual due recovery tick while the original Set is still held.
|
||||
// No replacement serialization, no paused production recovery algorithm.
|
||||
player.CoRecoveryEnergy(tables.TbGlobalConfig.InitEnergyRestorationSpeed*60);
|
||||
Check((int)player.Energy==initialEnergy-2 && !action.Confirmed,"real recovery can write during pending");
|
||||
fixture.ReleaseSet.TrySetResult(true);
|
||||
}
|
||||
await consumption; pending=null;
|
||||
Check((int)player.Energy==initialEnergy-(fixture.HoldSet?2:3) && player.GetEnergyRM()==10 && consumed==3,"one real deduction and event");
|
||||
Check(notified==(scenario=="cross-limit"?2:fixture.HoldSet?3:1),"original notification count preserved");
|
||||
var modern=JsonConvert.DeserializeObject<Dictionary<string,string>>(fixture.Remote["NewPlayerDataDic"]);
|
||||
Check(modern["newData"].Split(',')[3]==(initialEnergy-3).ToString() && !modern.ContainsKey("energy"),"one canonical representation");
|
||||
Check(fixture.Remote["k"]=="task-3" && action.Confirmed,"synchronous task write confirmed");
|
||||
Check(achievements.GetAchievementDatas(cfg.ConditionType.ConsumeEnergy)==3 && modern["ad"].Contains(":") && modern["ad"]==achievements.AchievementDataToJson(),"actual achievement subscriber captured after event");
|
||||
var savedTime=DateTime.Parse(modern["newData"].Split(',')[7]);
|
||||
Check(scenario=="cross-limit"?savedTime>new DateTime(2026,1,1):savedTime==new DateTime(2026,1,1),"recovery timestamp crossing behavior preserved");
|
||||
if(fixture.HoldSet)
|
||||
{
|
||||
await KVMgr.FlushKVAsync(action.Owner);
|
||||
var recovered=JsonConvert.DeserializeObject<Dictionary<string,string>>(fixture.Remote["NewPlayerDataDic"]);
|
||||
Check(recovered["newData"].Split(',')[3]==(initialEnergy-2).ToString(),"later recovery has separate flush, not original action confirmation");
|
||||
}
|
||||
if(subscribers!=null)
|
||||
{
|
||||
Check(subscribers.Battle.Data.BattleTask.dic_dailyTasks.Values.Single()==3 && subscribers.Lucky.DicTasks.Values.Single()==3 && subscribers.Magic.DicTasks.Values.Single()==3,"actual task subscriptions each receive one consume event");
|
||||
Check(subscribers.Mini.Data.Exp==3,"actual mini BP subscription receives the event");
|
||||
Check(new[]{"BattlePassRecordTask","MiniBPKeyNew","EventLuckGameRecord","EventLuckMagicPlayfabData"}.All(fixture.Remote.ContainsKey),"all actual subscriber save methods confirmed in original action");
|
||||
}
|
||||
var parsed=new PlayerKVSchemaSnapshot("fixed-fishing",tables.TbRodData.DataMap.Keys,tables.TbMapData.DataMap.Keys,
|
||||
new Dictionary<string,int>(),System.Globalization.CultureInfo.CurrentCulture,new DateTime(2026,9,24,0,0,0,DateTimeKind.Utc),0);
|
||||
var errors=new PlayerKVSchemaDiagnostics();
|
||||
Check(PlayerKVSaveSchema.DecodeCore(new Dictionary<string,string>(),modern,parsed,errors)!=null && errors.TotalErrors==0,"actual serialized core loads without alias conflict");
|
||||
}
|
||||
}
|
||||
result=JsonConvert.SerializeObject(new{success=true,groups,assertions=checks,passed,formalRequests=0,synthetic=true,restored=true,folder});
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
subscribers?.Dispose(); subscribers=null;
|
||||
player?.StopRecoveryEnergy(); subscription?.Dispose();
|
||||
if(fixture!=null) await fixture.Close();
|
||||
if(pending!=null) { try { await pending; } catch(Exception e) { Debug.LogWarning("Observed fishing test failure: "+e.GetType().Name); } }
|
||||
FishingContextAccess.Use(oldContainer); FishingContextAccess.Events=oldEvents; container?.Dispose();
|
||||
LocalizationMgr.enumLanguage=oldLanguages; textTable.SetValue(null,oldTextTable);
|
||||
(Enabled,Ready,Error,Permit,RunPath,handle,onFault,onClose,collecting,mainThread,failed,mainContext,fishingAction)=saved;
|
||||
}
|
||||
catch { validationCleanupFailure=fixture; throw; }
|
||||
finally { Interlocked.Exchange(ref validationRunning,0); }
|
||||
}
|
||||
File.WriteAllText(Path.Combine(folder,"result.json"),result); return result;
|
||||
}
|
||||
// Fixed real subscriber fixture. Reflection targets are compiled constants, never supplied by CLI.
|
||||
sealed class FishingSubscribers:IDisposable
|
||||
{
|
||||
const System.Reflection.BindingFlags Fields=System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic;
|
||||
static readonly System.Reflection.FieldInfo PF=typeof(PlayFabMgr).GetField("_instance",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic);
|
||||
static readonly System.Reflection.FieldInfo Red=typeof(RedPointManager).GetField("instance",System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.NonPublic);
|
||||
readonly object oldRed=Red.GetValue(null),oldPF=PF.GetValue(null);
|
||||
GameObject shell; LuckyTaskData magicTask; MiniBattlePassDataProvider provider;
|
||||
internal BattlePassDataProvider Battle; internal EventLuckyGameModel Lucky;
|
||||
internal EventLuckMagicModel Magic; internal MiniBattlePassDataModel Mini;
|
||||
internal void Bind(cfg.Tables tables,FishingEventData events,Container container)
|
||||
{
|
||||
Red.SetValue(null,new RedPointManager());
|
||||
shell=new GameObject("KV inactive subscriber storage fixture");shell.SetActive(false);shell.hideFlags=HideFlags.HideAndDontSave;
|
||||
var pf=shell.AddComponent<PlayFabMgr>();
|
||||
if(!pf.IsUnstartedForKVLoad)throw new InvalidOperationException("No PF lifecycle may start in this fixture.");
|
||||
PF.SetValue(null,pf);
|
||||
var items=new PlayerItemData(tables);container.RegisterInstance(items);
|
||||
var data=new BattlePassData(tables);
|
||||
data.BattlePassRecord.ID=tables.TbFishingEvent.DataList.First(e=>tables.TbBattlePassMain.GetOrDefault(e.RedirectID)!=null).ID;
|
||||
data.BattlePassRecord.Level=1;
|
||||
data.BattleTask.dic_dailyTasks.Add(tables.TbBattlePassTask.DataList.First(t=>t.Type==cfg.ConditionType.ConsumeEnergy).TaskID,0);
|
||||
Battle=new BattlePassDataProvider(tables,data,items);container.RegisterInstance(Battle);
|
||||
var common=tables.TbCommonTask.DataList.First(t=>t.TaskType==cfg.ConditionType.ConsumeEnergy).Id;
|
||||
Lucky=new EventLuckyGameModel(events,tables);Lucky.DicTasks.Add(common,0);
|
||||
Magic=new EventLuckMagicModel{DicTasks=new Dictionary<int,int>{{common,0}},HigherRingData=EventLuckMagicHigherRingData.CreateEmpty()};
|
||||
var rings=(List<EventLuckMagicRing>)typeof(EventLuckMagicModel).GetField("_rings",Fields).GetValue(Magic);
|
||||
for(int i=1;i<=3;i++)rings.Add(EventLuckMagicRing.CreateEmpty(1,0,(EEventLuckMagicRingType)i));
|
||||
magicTask=new LuckyTaskData(Magic);typeof(EventLuckMagicModel).GetField("_taskData",Fields).SetValue(Magic,magicTask);
|
||||
provider=new MiniBattlePassDataProvider(tables,items);
|
||||
var table=tables.TbMiniBattlePass.DataList.First(t=>t.TaskType==cfg.ConditionType.ConsumeEnergy && t.Param==0);
|
||||
var activity=tables.TbFishingEvent.DataList.First(t=>t.Type==9 && t.RedirectID==table.Id);
|
||||
var type=(MiniBattlePassType)table.Type;
|
||||
Mini=provider.GetModel(type);
|
||||
provider.GetMiniBattlePassRecord("BP_"+type).Id=activity.ID;
|
||||
typeof(MiniBattlePassDataModel).GetField("fishingEvent",Fields).SetValue(Mini,activity);
|
||||
typeof(MiniBattlePassDataModel).GetMethod("InitData",Fields).Invoke(Mini,new object[]{table.Id});
|
||||
// Synthetic fixture has an active window; real-run time is never modified.
|
||||
Mini.startTime=ZZTimeHelper.UtcNow().AddDays(-1);
|
||||
typeof(MiniBattlePassDataModel).GetProperty("endTime").SetValue(Mini,ZZTimeHelper.UtcNow().AddDays(1));
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Battle?.Dispose();Lucky?.Dispose();magicTask?.Dispose();
|
||||
if(Mini!=null)(typeof(MiniBattlePassDataModel).GetField("disposables",Fields).GetValue(Mini) as IDisposable)?.Dispose();
|
||||
PF.SetValue(null,oldPF);Red.SetValue(null,oldRed);
|
||||
if(shell!=null)UnityEngine.Object.DestroyImmediate(shell);
|
||||
}
|
||||
}
|
||||
sealed class FishingContextAccess:GContext
|
||||
{
|
||||
protected override Task SetupAsync()=>throw new InvalidOperationException("Test-only context.");
|
||||
internal static void Use(Container value) { container=value; }
|
||||
internal static IEventAggregator Events { get=>eventAggregator; set=>eventAggregator=value; }
|
||||
}
|
||||
sealed class FishingNotifications:ILocalNotificationService
|
||||
{
|
||||
public Task Init()=>Task.CompletedTask; public bool IsNoticeOpen=>false;
|
||||
public void OpenSettings(){} public void ScheduleOneTimeNotification(int id,DateTime time,string title,string text){}
|
||||
public void ScheduleDailyNotification(int id,DateTime time,string title,string text){}
|
||||
public void ClearAllNotification(){} public void ClearNotification(int id){} public void ScheduleDefaultNotification(){}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using game.PlayerDataStorage;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public partial class PlayFabUserService
|
||||
{
|
||||
readonly bool kvControlledInstance;
|
||||
PlayerKVGameLoadCoordinator kvCoordinator;
|
||||
Action<IDictionary<string,string>> kvReceiver;
|
||||
int kvReceiverUsers;
|
||||
internal PlayerKVGameLoadError KVLastError { get; private set; }
|
||||
internal string CaptureSaveBackend() => config==null ? "playfab" : config.Get<string>("SaveBackend","playfab");
|
||||
internal bool RejectSaveBackend(string backend)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerKVGameplayLoop.Enabled && backend=="kv-loop" && !kvControlledInstance)
|
||||
{ KVLastError=PlayerKVGameLoadError.None; return false; }
|
||||
#endif
|
||||
KVLastError=backend=="kv" ? PlayerKVGameLoadError.BackendNotEnabled : backend!="playfab" ? PlayerKVGameLoadError.BackendUnsupported :
|
||||
kvControlledInstance ? PlayerKVGameLoadError.Input : PlayerKVGameLoadError.None;
|
||||
return KVLastError!=PlayerKVGameLoadError.None;
|
||||
}
|
||||
internal void ConfigureKVForValidation(PlayerKVGameLoadTransaction transaction,PlayFabMgr pf,CustomServerMgr server,Func<bool> unstarted)
|
||||
{
|
||||
if(!kvControlledInstance || kvCoordinator!=null || pf==null || !pf.IsUnstartedForKVLoad || PlayFabMgr.HasLiveInstanceForKVLoad ||
|
||||
server==null || !server.IsFreshForKVLoad || unstarted==null || !unstarted()) throw new InvalidOperationException("Fresh controlled services required.");
|
||||
kvCoordinator=new PlayerKVGameLoadCoordinator(transaction,()=>pf.IsUnstartedForKVLoad && InternalId==0 && unstarted(),
|
||||
NotifyKVLoaded,()=>KVIntents?.Count ?? 0,server.BindKVForValidation,server.ClearKVForValidation);
|
||||
server.KVUnauthorized+=version=> { if(version==server.SessionVersion) { InternalId=0; _=kvCoordinator.Revoke(); } };
|
||||
}
|
||||
internal virtual async Task<PlayerKVGameLoadResult> LoadKVForValidationAsync(PlayerKVGameLoadCapability capability)
|
||||
{
|
||||
if(!kvControlledInstance || kvCoordinator==null || capability==null)
|
||||
return new PlayerKVGameLoadResult(PlayerKVGameLoadError.Input,0);
|
||||
try
|
||||
{
|
||||
int loginMs=config==null ? 10000 : PlayerKVSaveSchema.Int32(config.Get<string>("KVLoginCloseObserveMs","10000"));
|
||||
int quitMs=config==null ? 5000 : PlayerKVSaveSchema.Int32(config.Get<string>("KVQuitCloseObserveMs","5000"));
|
||||
if(loginMs!=capability.LoginObserveMs || quitMs!=capability.QuitObserveMs || loginMs<1000 || loginMs>15000 || quitMs<1000 || quitMs>15000)
|
||||
return new PlayerKVGameLoadResult(PlayerKVGameLoadError.Input,0);
|
||||
}
|
||||
catch { return new PlayerKVGameLoadResult(PlayerKVGameLoadError.Input,0); }
|
||||
var requestedId=CustomId; var generation=logoutGeneration;
|
||||
var result=await kvCoordinator.LoadAsync(requestedId,capability,LoginLifetime,()=>requestedId==CustomId && generation==logoutGeneration);
|
||||
if(generation!=logoutGeneration || requestedId!=CustomId)
|
||||
{
|
||||
_=kvCoordinator.Revoke();
|
||||
if(result.State==PlayerKVGameLoadState.LoadedForValidation) result=new PlayerKVGameLoadResult(PlayerKVGameLoadError.Stale,result.Epoch);
|
||||
}
|
||||
KVLastError=result.Error; return result;
|
||||
}
|
||||
internal Task CloseKVForValidationAsync() => kvCoordinator?.Revoke() ?? Task.CompletedTask;
|
||||
internal Task<PlayerKVShutdownStatus> ObserveKVQuitAsync() => kvCoordinator?.ObserveQuitAsync() ?? Task.FromResult(PlayerKVShutdownStatus.Ready);
|
||||
internal bool KVLoadedForValidation => kvCoordinator?.LoadedForValidation ?? false;
|
||||
internal int KVReceiverCountForValidation => kvReceiverUsers;
|
||||
internal void EnterKVReceiver(Action<IDictionary<string,string>> receiver)
|
||||
{
|
||||
if(!kvControlledInstance || receiver==null || (kvReceiver!=null && kvReceiver!=receiver)) throw new InvalidOperationException("Controlled receiver required.");
|
||||
if(kvReceiverUsers++==0) { kvReceiver=receiver; OnInitData+=receiver; }
|
||||
}
|
||||
internal void ExitKVReceiver()
|
||||
{ if(--kvReceiverUsers==0) { OnInitData-=kvReceiver; kvReceiver=null; } }
|
||||
void NotifyKVLoaded()
|
||||
{
|
||||
var listeners=OnInitData?.GetInvocationList();
|
||||
if(listeners!=null && (listeners.Length!=1 || kvReceiver==null || !listeners[0].Equals(kvReceiver)))
|
||||
throw new InvalidOperationException("Unregistered controlled receiver.");
|
||||
OnInitData?.Invoke(KVLoadingCopy.ToDictionary(p=>p.Key,p=>p.Value,StringComparer.Ordinal));
|
||||
}
|
||||
internal int KVTapJoyBalance { get; private set; }
|
||||
internal PlayerKVInitializationIntents KVIntents { get; private set; }
|
||||
internal IReadOnlyDictionary<string,string> KVLoadingCopy { get; private set; }
|
||||
internal int MMTestForKVValidation => MMTestValue;
|
||||
internal bool IsControlledKVInstance => kvControlledInstance;
|
||||
|
||||
internal virtual void ResetForKVLoad()
|
||||
{
|
||||
bool admitted=kvControlledInstance, sessionAllowed=apiSession==null;
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
admitted=admitted || KVLoopLoading;
|
||||
sessionAllowed=sessionAllowed || KVLoopLoading;
|
||||
#endif
|
||||
if(!admitted || InternalId!=0 || !sessionAllowed)
|
||||
throw new InvalidOperationException("Unstarted controlled identity required.");
|
||||
KVIntents?.Discard(); KVIntents=null; KVLoadingCopy=null;
|
||||
MMTestValue=0; EAccoutUrl=new Dictionary<string,string>(StringComparer.Ordinal); KVTapJoyBalance=0;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(PlayerKVLoginState state,long epoch,IReadOnlyDictionary<string,string> raw)
|
||||
{
|
||||
bool admitted=kvControlledInstance;
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
admitted=admitted || KVLoopLoading;
|
||||
#endif
|
||||
if(!admitted || state==null || raw==null) throw new InvalidOperationException("Controlled login state required.");
|
||||
var copy=raw.ToDictionary(p=>p.Key,p=>p.Value,StringComparer.Ordinal);
|
||||
var intents=new PlayerKVInitializationIntents(epoch,state.Channel);
|
||||
foreach(var pair in state.Normalizations) { copy[pair.Key]=pair.Value; intents.Add(epoch,pair.Key,pair.Value); }
|
||||
KVIntents?.Discard(); KVIntents=intents;
|
||||
KVLoadingCopy=new System.Collections.ObjectModel.ReadOnlyDictionary<string,string>(copy);
|
||||
MMTestValue=state.MMTest; KVTapJoyBalance=state.TapJoyBalance;
|
||||
EAccoutUrl=state.AccountUrls.ToDictionary(p=>p.Key,p=>p.Value,StringComparer.Ordinal);
|
||||
ReplaceKVLinked(state);
|
||||
}
|
||||
void IKVUserLoadReceiver.ResetForKVLoad() => ResetForKVLoad();
|
||||
void IKVUserLoadReceiver.ReplaceFromKVDecoded(PlayerKVLoginState state,long epoch,IReadOnlyDictionary<string,string> raw)
|
||||
=> ReplaceFromKVDecoded(state,epoch,raw);
|
||||
void IKVUserLoadReceiver.DiscardKVIntents() => KVIntents?.Discard();
|
||||
bool IKVUserLifecycle.KVHandoffReady => false;
|
||||
PlayerKVGameLoadError IKVUserLifecycle.KVLastError => KVLastError;
|
||||
Task IKVUserLifecycle.CloseKVForValidationAsync() => CloseKVForValidationAsync();
|
||||
Task<PlayerKVShutdownStatus> IKVUserLifecycle.ObserveKVQuitAsync() => ObserveKVQuitAsync();
|
||||
internal virtual void ReplaceKVLinked(PlayerKVLoginState state) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,914 @@
|
||||
using System.Threading.Tasks;
|
||||
using game.Account;
|
||||
using asap.core;
|
||||
using PlayFab;
|
||||
using asap.playfab.async;
|
||||
using PlayFab.ClientModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
#if UNITY_ANDROID
|
||||
using TapjoyUnity.Internal;
|
||||
#endif
|
||||
|
||||
using tysdk;
|
||||
using UniRx;
|
||||
using YiDun.Protector;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public enum ELoginStep
|
||||
{
|
||||
PlayFab,
|
||||
Sdk,
|
||||
Api,
|
||||
Done
|
||||
}
|
||||
|
||||
public class LoginRequest
|
||||
{
|
||||
public string playerId { get; set; }
|
||||
public PlayerProfile playerProfile { get; set; }
|
||||
}
|
||||
|
||||
public class PlayerProfile
|
||||
{
|
||||
public int Level { get; set; }
|
||||
public string Avatar { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
}
|
||||
|
||||
public class EvtAPISvrLoginSuccess
|
||||
{
|
||||
public string playerId { get; set; }
|
||||
public uint internalId { get; set; }
|
||||
}
|
||||
|
||||
public class EvtAPISvrUnauthorized { public int SessionVersion { get; set; } }
|
||||
|
||||
public interface IUserService
|
||||
{
|
||||
Task<ELoginStep> Login(Action<int> progress, bool tyrLoginByToken = true);
|
||||
void Logout();
|
||||
System.Threading.CancellationToken LoginLifetime { get; }
|
||||
bool IsLogin { get; }
|
||||
bool IsAuthorized { get; }
|
||||
DateTime LastLoginTime { get; }
|
||||
DateTime LoginTime { get; }
|
||||
DateTime CreateTime { get; }
|
||||
Task<string> ChangeDisplayName(string newName);
|
||||
string DisplayName { get; }
|
||||
string AvatarUrl { get; }
|
||||
Dictionary<string, string> EAccoutUrl { get; }
|
||||
Task<string> UpdateAvatarUrl(string url);
|
||||
string UserId { get; }
|
||||
string ApiPlayerId { get; }
|
||||
string CustomId { get; set; }
|
||||
uint InternalId { get; }
|
||||
string ContinentCode { get; }
|
||||
//string CountryCode { get; }
|
||||
double CreateTotalSeconds();
|
||||
string Language { get; }
|
||||
Task<string> UpdateLanguage(string code);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Link sns account
|
||||
/// success return ( code 0, current userid)
|
||||
/// exists return ( code -1, exists account user id)
|
||||
/// fail return ( code 1, 0 )
|
||||
/// </summary>
|
||||
/// <param name="accoutType"> sns type [hwGoogle, hwFacebook, hwGuest, Apple]</param>
|
||||
/// <returns>code and userid</returns>
|
||||
Task<(int code, int userid)> LinkAccount(EAccoutType accoutType);
|
||||
Task<bool> GetLinkState(EAccoutType accoutType);
|
||||
void SetPlayerLv(string playFabId, int lv);
|
||||
Task<(string, int)> GetPlayLv(string playFabId);
|
||||
int GetPlayerLv(string playFabId);
|
||||
|
||||
PlayInfo GetPlayInfo(string id);
|
||||
void ClearPlayInfos();
|
||||
void SetPlayInfo(string playFabId, string name, string avatarUrl);
|
||||
string GetDefaultName(string id);
|
||||
void UpdateProfile();
|
||||
void UpdateProfileLv();
|
||||
}
|
||||
|
||||
public partial class PlayFabUserService : IUserService, PlayerDataStorage.IKVUserLoadReceiver, PlayerDataStorage.IKVUserLifecycle
|
||||
{
|
||||
[Inject]
|
||||
public IConfig config { get; set; }
|
||||
|
||||
[Inject]
|
||||
public ICustomServerMgr customServerMgr { get; set; }
|
||||
|
||||
public bool IsLogin => !kvControlledInstance && PlayFabClientAPI.IsClientLoggedIn() && IsAuthorized;
|
||||
|
||||
public bool IsAuthorized => InternalId != 0 && apiSession != null && apiSession.IsAuthenticated;
|
||||
|
||||
// UserId remains the legacy storage identity until the KV migration.
|
||||
public string ApiPlayerId => IsAuthorized ? apiSession.PlayerId : null;
|
||||
|
||||
public DateTime LastLoginTime { get; protected set; }
|
||||
|
||||
public DateTime LoginTime { get; protected set; }
|
||||
|
||||
public DateTime CreateTime { get; protected set; }
|
||||
|
||||
public string DisplayName { get; protected set; }
|
||||
|
||||
public string AvatarUrl { get; protected set; }
|
||||
public Dictionary<string, string> EAccoutUrl { get; protected set; }
|
||||
public string Language { get; protected set; }
|
||||
|
||||
public string UserId { get; protected set; }
|
||||
public EntityKey entityKey { get; protected set; }
|
||||
public string CustomId { get; set; }
|
||||
public uint InternalId { get; protected set; }
|
||||
public string ContinentCode { get; protected set; }
|
||||
public string CountryCode { get; protected set; }
|
||||
protected virtual string NewPlayerName => $"Angler#{CustomId}";
|
||||
|
||||
protected Action<IDictionary<string, string>> OnInitData;
|
||||
|
||||
protected virtual string NewPlayerAvatar => string.Empty;
|
||||
private bool IsApiServerLogin => InternalId != 0;
|
||||
int MMTestValue;//0=>A 原来的 对照组 1=>B 新的实验组
|
||||
const string MMTestKey = "MMTest";
|
||||
const string MMTestSubKey = "MMFailParam";
|
||||
public string GetDefaultName(string id)
|
||||
{
|
||||
return "Angler#" + (string.IsNullOrEmpty(id) ? string.Empty : id.Substring(0, Math.Min(5, id.Length)));
|
||||
}
|
||||
|
||||
public PlayFabUserService() : this(null) { }
|
||||
|
||||
internal PlayFabUserService(PlayerDataStorage.PlayerKVControlledLoadCapability capability)
|
||||
{
|
||||
kvControlledInstance = capability != null;
|
||||
if (kvControlledInstance) return;
|
||||
GContext.OnEvent<EvtAPISvrUnauthorized>().Subscribe(evt =>
|
||||
{
|
||||
if (evt.SessionVersion != customServerMgr.SessionVersion) return;
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) { game.PlayerDataStorage.PlayerKVGameplayLoop.Fault("API unauthorized; restart required."); return; }
|
||||
#endif
|
||||
InternalId = 0;
|
||||
if (PlayFabClientAPI.IsClientLoggedIn()) _ = LoginApiServer();
|
||||
});
|
||||
}
|
||||
|
||||
protected int logoutGeneration;
|
||||
private System.Threading.CancellationTokenSource loginLifetime = new System.Threading.CancellationTokenSource();
|
||||
public System.Threading.CancellationToken LoginLifetime => loginLifetime.Token;
|
||||
private readonly System.Threading.SemaphoreSlim loginGate = new System.Threading.SemaphoreSlim(1, 1);
|
||||
public virtual Task<ELoginStep> Login(Action<int> progress, bool tyrLoginByToken = true) => LoginWithBackend(progress,CaptureSaveBackend());
|
||||
|
||||
protected async Task<ELoginStep> LoginWithBackend(Action<int> progress,string backend)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
var requestedId = CustomId;
|
||||
if(RejectSaveBackend(backend)) return ELoginStep.Api;
|
||||
await loginGate.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (generation != logoutGeneration || requestedId != CustomId) return ELoginStep.Api;
|
||||
if (!long.TryParse(requestedId, System.Globalization.NumberStyles.None,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out var numericId) || numericId <= 0)
|
||||
{
|
||||
return ELoginStep.Api;
|
||||
}
|
||||
return await LoginCore(progress, generation);
|
||||
}
|
||||
finally { loginGate.Release(); }
|
||||
}
|
||||
|
||||
private async Task<ELoginStep> LoginCore(Action<int> progress, int generation)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) return await LoginKVGameplayLoopAsync(progress,generation);
|
||||
#endif
|
||||
if (IsLogin)
|
||||
{
|
||||
progress?.Invoke(2);
|
||||
return ELoginStep.Done;
|
||||
}
|
||||
|
||||
if (!PlayFabClientAPI.IsClientLoggedIn())
|
||||
{
|
||||
if (!await LoginPlayFab(generation))
|
||||
return ELoginStep.PlayFab;
|
||||
}
|
||||
|
||||
if (generation != logoutGeneration) return ELoginStep.Api;
|
||||
|
||||
SetMMTestValue();
|
||||
|
||||
GContext.container.Resolve<IChatService>().WaitingForLogin();
|
||||
|
||||
OnYinDunLogin();
|
||||
if (!await LoginApiServer() || generation != logoutGeneration)
|
||||
return ELoginStep.Api;
|
||||
|
||||
InitTapJoy();
|
||||
progress?.Invoke(2);
|
||||
return ELoginStep.Done;
|
||||
}
|
||||
|
||||
private static void InitTapJoy()
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
var lastBalanceStr = PlayFabMgr.Instance.GetLocalData("TapJoy_Diamond") ?? "";
|
||||
Debug.Log($"InitTapJoy -> {lastBalanceStr}");
|
||||
var lastBalance = int.TryParse(lastBalanceStr, out var temp) ? temp : 0;
|
||||
|
||||
var tapjoyObject = GameObject.FindObjectOfType<TapjoyComponent>();
|
||||
if (tapjoyObject && tapjoyObject.gameObject && !tapjoyObject.GetComponent<TapjoyHelper>())
|
||||
{
|
||||
var tapjoyHelper = tapjoyObject.gameObject.AddComponent<TapjoyHelper>();
|
||||
GameObject.DontDestroyOnLoad(tapjoyHelper);
|
||||
tapjoyHelper.Init(lastBalance, OnEarnCurrencyName, OnBalance);
|
||||
}
|
||||
return;
|
||||
void OnEarnCurrencyName(string currencyName, int amount)
|
||||
{
|
||||
Debug.Log($"OnEarnCurrencyName -> {currencyName} {amount}");
|
||||
// var amount = (balance - lastBalance);
|
||||
var diamonds = new ItemData { id = 1005, count = amount };
|
||||
diamonds.curCount = (ulong)GContext.container.Resolve<PlayerItemData>().GetItemCount(diamonds.id);
|
||||
GContext.container.Resolve<PlayerItemData>().AddItem(diamonds, null);
|
||||
GContext.Publish(new ShowData(new List<ItemData> { diamonds }, RewardType.NormalOne));
|
||||
GContext.Publish(new ShowData());
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("tapjoy"))
|
||||
{
|
||||
e.AddContent("item_id", diamonds.id)
|
||||
.AddContent("item_count", diamonds.count);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
void OnBalance(string currencyName, int balance)
|
||||
{
|
||||
Debug.Log($"OnBalance -> {currencyName} {balance}");
|
||||
// const string tapjoyKey = "TapJoy_Diamond";
|
||||
// var lastBalanceStr = PlayFabMgr.Instance.GetLocalData(tapjoyKey) ?? "";
|
||||
// var lastBalance = int.TryParse(lastBalanceStr, out var temp) ? temp : 0;
|
||||
{
|
||||
// PlayFabMgr.Instance.UpdateUserDataValue(tapjoyKey, balance.ToString());
|
||||
// if (balance > lastBalance)
|
||||
// {
|
||||
// var amount = (balance - lastBalance);
|
||||
// var diamonds = new ItemData { id = 1005, count = amount };
|
||||
// diamonds.curCount = (ulong)GContext.container.Resolve<PlayerItemData>().GetItemCount(diamonds.id);
|
||||
// GContext.container.Resolve<PlayerItemData>().AddItem(diamonds);
|
||||
// GContext.Publish(new ShowData(new List<ItemData> { diamonds }, RewardType.NormalOne));
|
||||
// // GContext.Publish(new ShowData(diamonds));
|
||||
// GContext.Publish(new ShowData());
|
||||
// }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
protected async void OnYinDunLogin()
|
||||
{
|
||||
// 开发模式下,先不启用了
|
||||
if (config != null && (config.Get("DEBUG", "0") == "1" || config.Get("GM", "0") == "1"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var userService = GContext.container.Resolve<IUserService>();
|
||||
var yiDunManager = GContext.container.Resolve<YiDunManager>();
|
||||
var roleName = userService.DisplayName;
|
||||
var roleId = userService.UserId;
|
||||
var roleAccount = userService.CustomId;
|
||||
var versionData = new
|
||||
{
|
||||
GameVersion = Application.version,
|
||||
AssetVersion = config.Get(GConstant.K_AssetVersion, string.Empty),
|
||||
#if UNITY_ANDROID
|
||||
Channel = GConstant.Yidun_GooglePlayChannel
|
||||
#elif UNITY_IOS
|
||||
Channel = GConstant.Yidun_IOSChannel
|
||||
#endif
|
||||
};
|
||||
Debug.Log($"JsonUtility.ToJson(versionData) => {JsonConvert.SerializeObject(versionData)}");
|
||||
|
||||
var businessId = GConstant.Yidun_AndroidBusinessId;
|
||||
#if UNITY_IOS
|
||||
businessId = GConstant.Yidun_IOSBusinessId;
|
||||
#endif
|
||||
|
||||
#if (UNITY_ANDROID || UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR
|
||||
yiDunManager.SetRoleInfo(businessId, roleId, roleName, roleAccount, null, -1, JsonConvert.SerializeObject(versionData));
|
||||
yiDunManager.PostRiskCheck();
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetMMTestValue()
|
||||
{
|
||||
if (MMTestValue == -1)
|
||||
{
|
||||
//int id = (int)(InternalId / 2) * 2 + 1;
|
||||
//int AB = (int)(id % 8);
|
||||
// convert UserId string to ulong
|
||||
var ulongUserId = ulong.Parse(UserId, System.Globalization.NumberStyles.HexNumber);
|
||||
int AB = (int)(ulongUserId % 16);
|
||||
MMTestValue = AB;
|
||||
PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
}
|
||||
string ab = GContext.container.Resolve<MMTService>().Init(MMTestValue);
|
||||
GEvent.SetUserProp("MMTestValue", ab, true);
|
||||
Debug.Log($"UserId:{UserId} MMTestValue :0b{Convert.ToString(MMTestValue, 2)} MMTestValueString:{ab}");
|
||||
}
|
||||
public void GMMMT(int value)
|
||||
{
|
||||
MMTestValue = value;
|
||||
PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
string ab = GContext.container.Resolve<MMTService>().Init(MMTestValue);
|
||||
GEvent.SetUserProp("MMTestValue", ab, true);
|
||||
Debug.Log($"UserId:{UserId} MMTestValue :0b{Convert.ToString(MMTestValue, 2)} MMTestValueString:{ab}");
|
||||
}
|
||||
private async Task<bool> LoginPlayFab(int generation)
|
||||
{
|
||||
PlayFabSettings.TitleId = config.Get<string>(GConstant.K_PlayFab_Title, GConstant.V_Debug_PF_Title);
|
||||
PlayFab.Internal.PlayFabWebRequest.SkipCertificateValidation();
|
||||
|
||||
var timeout = config.Get<int>(GConstant.K_PlayFab_TimeOut, 0);
|
||||
if (timeout != 0)
|
||||
{
|
||||
PlayFabSettings.RequestTimeout = timeout;
|
||||
}
|
||||
|
||||
var sw = new System.Diagnostics.Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
var info_request_parameters = new GetPlayerCombinedInfoRequestParams
|
||||
{
|
||||
GetUserAccountInfo = true,
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
GetUserData = !game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled,
|
||||
#else
|
||||
GetUserData = true,
|
||||
#endif
|
||||
GetPlayerProfile = true,
|
||||
ProfileConstraints = new PlayerProfileViewConstraints()
|
||||
{
|
||||
ShowAvatarUrl = true,
|
||||
ShowDisplayName = true,
|
||||
ShowLocations = true,
|
||||
}
|
||||
};
|
||||
|
||||
LoginResult loginResult = null;
|
||||
|
||||
|
||||
if (CustomId != string.Empty)
|
||||
{
|
||||
var request = new LoginWithCustomIDRequest
|
||||
{
|
||||
CustomId = CustomId,
|
||||
CreateAccount = true,
|
||||
InfoRequestParameters = info_request_parameters,
|
||||
};
|
||||
Debug.Log($"[UserService::LoginPlayFab] LoginWithCustomIDAsync customId:{CustomId}");
|
||||
loginResult = await PlayFabClientAsyncAPI.ExecuteWithRetryAsync(
|
||||
() => PlayFabClientAsyncAPI.LoginWithCustomIDAsync(request));
|
||||
Debug.Log($"[UserService::LoginPlayFab] LoginWithCustomIDAsync result:{loginResult != null}");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#if UNITY_IOS
|
||||
CustomId = UnityEngine.iOS.Device.advertisingIdentifier;
|
||||
var request = new LoginWithIOSDeviceIDRequest
|
||||
{
|
||||
DeviceId = UnityEngine.iOS.Device.advertisingIdentifier,
|
||||
OS = "iOS",
|
||||
DeviceModel = UnityEngine.iOS.Device.generation.ToString(),
|
||||
CreateAccount = true,
|
||||
InfoRequestParameters = info_request_parameters,
|
||||
};
|
||||
|
||||
loginResult = await PlayFabClientAsyncAPI.LoginWithIOSDeviceIDAsync(request);
|
||||
|
||||
#elif UNITY_ANDROID
|
||||
|
||||
CustomId = SystemInfo.deviceUniqueIdentifier;
|
||||
var request = new LoginWithAndroidDeviceIDRequest
|
||||
{
|
||||
AndroidDeviceId = SystemInfo.deviceUniqueIdentifier,
|
||||
AndroidDevice = SystemInfo.deviceModel,
|
||||
CreateAccount = true,
|
||||
InfoRequestParameters = info_request_parameters,
|
||||
};
|
||||
|
||||
loginResult = await PlayFabClientAsyncAPI.LoginWithAndroidDeviceIDAsync(request);
|
||||
|
||||
#else
|
||||
CustomId = Application.identifier;
|
||||
var request = new LoginWithCustomIDRequest
|
||||
{
|
||||
AndroidDeviceId = Application.identifier,
|
||||
CreateAccount = true,
|
||||
InfoRequestParameters = info_request_parameters,
|
||||
};
|
||||
loginResult = await PlayFabClientAsyncAPI.ExecuteWithRetryAsync(
|
||||
() => PlayFabClientAsyncAPI.LoginWithCustomIDAsync(request));
|
||||
#endif
|
||||
}
|
||||
sw.Stop();
|
||||
if (generation != logoutGeneration)
|
||||
{
|
||||
PlayFabClientAPI.ForgetAllCredentials();
|
||||
return false;
|
||||
}
|
||||
if (loginResult == null) return false;
|
||||
|
||||
Debug.Log($"LoginPlayFab elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
|
||||
|
||||
entityKey = loginResult.EntityToken.Entity;
|
||||
if (!loginResult.NewlyCreated)
|
||||
{
|
||||
DisplayName = loginResult.InfoResultPayload.PlayerProfile?.DisplayName ?? string.Empty;
|
||||
AvatarUrl = loginResult.InfoResultPayload.PlayerProfile?.AvatarUrl ?? string.Empty;
|
||||
ContinentCode = loginResult.InfoResultPayload.PlayerProfile?.Locations[0]?.ContinentCode?.ToString() ?? "UNKNOW";
|
||||
CountryCode = loginResult.InfoResultPayload.PlayerProfile?.Locations[0]?.CountryCode?.ToString();
|
||||
|
||||
config.Set("NewlyCreated", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
sw.Start();
|
||||
|
||||
GetPlayerProfileRequest getPlayerProfileRequest = new GetPlayerProfileRequest()
|
||||
{
|
||||
ProfileConstraints = new PlayerProfileViewConstraints()
|
||||
{
|
||||
//ShowAvatarUrl = true,
|
||||
//ShowDisplayName = true,
|
||||
ShowLocations = true,
|
||||
}
|
||||
};
|
||||
var data = await PlayFabClientAsyncAPI.GetPlayerProfileAsync(getPlayerProfileRequest);
|
||||
if (generation != logoutGeneration) return false;
|
||||
//DisplayName = data.PlayerProfile.DisplayName;
|
||||
//AvatarUrl = data.PlayerProfile.AvatarUrl;
|
||||
ContinentCode = data?.PlayerProfile.Locations[0]?.ContinentCode?.ToString() ?? "UNKNOW";
|
||||
CountryCode = data?.PlayerProfile.Locations[0]?.CountryCode?.ToString();
|
||||
sw.Stop();
|
||||
Debug.Log($"LoginPlayFab GetPlayerProfile elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
|
||||
config.Set("NewlyCreated", true);
|
||||
}
|
||||
|
||||
UserId = loginResult.PlayFabId;
|
||||
if (string.IsNullOrEmpty(DisplayName))
|
||||
{
|
||||
sw.Start();
|
||||
var newDisplayName = await ChangeDisplayName(NewPlayerName);
|
||||
if (generation != logoutGeneration) return false;
|
||||
DisplayName = newDisplayName;
|
||||
sw.Stop();
|
||||
Debug.Log($"LoginPlayFab ChangeDisplayName elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(AvatarUrl))
|
||||
{
|
||||
sw.Start();
|
||||
/*AvatarUrl = */
|
||||
await UpdateAvatarUrl(NewPlayerAvatar);
|
||||
if (generation != logoutGeneration) return false;
|
||||
sw.Stop();
|
||||
Debug.Log($"LoginPlayFab update avatar elapsed={sw.Elapsed.TotalSeconds}");
|
||||
sw.Reset();
|
||||
}
|
||||
//BuglyAgent.SetUserId(UserId);
|
||||
var setting = GContext.container.Resolve<ISettingService>();
|
||||
await UpdateLanguage(setting.Lang);
|
||||
if (generation != logoutGeneration) return false;
|
||||
|
||||
var playfabUserData = loginResult.InfoResultPayload?.UserData;
|
||||
|
||||
CreateTime = loginResult.InfoResultPayload.AccountInfo.Created.ToUniversalTime();
|
||||
var tokenExpiration = loginResult.EntityToken.TokenExpiration?.ToUniversalTime();
|
||||
if (tokenExpiration.HasValue)
|
||||
{
|
||||
LoginTime = tokenExpiration.Value.ToUniversalTime() - TimeSpan.FromDays(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LoginTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if (loginResult.LastLoginTime.HasValue)
|
||||
LastLoginTime = loginResult.LastLoginTime.Value.ToUniversalTime();
|
||||
|
||||
ZZTimeHelper.AdjustTime(LoginTime);
|
||||
ZZTimeHelper.WathForcusChange();
|
||||
|
||||
//InitData should invoke after local time adjusted.
|
||||
//save login time to local storage.
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(!game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled)
|
||||
#endif
|
||||
InitData(playfabUserData, UserId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private S101AccountSession apiSession;
|
||||
private Task<bool> apiLoginTask;
|
||||
private int apiLoginGeneration;
|
||||
|
||||
private Task<bool> LoginApiServer()
|
||||
{
|
||||
if (apiLoginTask != null && !apiLoginTask.IsCompleted)
|
||||
return apiLoginTask;
|
||||
apiLoginTask = LoginApiServerCore();
|
||||
return apiLoginTask;
|
||||
}
|
||||
|
||||
private async Task<bool> LoginApiServerCore()
|
||||
{
|
||||
var generation = ++apiLoginGeneration;
|
||||
var requestedId = CustomId;
|
||||
InternalId = 0;
|
||||
apiSession?.Dispose();
|
||||
apiSession = null;
|
||||
try
|
||||
{
|
||||
customServerMgr.ClearAccountSession();
|
||||
var session = new S101AccountSession(customServerMgr.ApiBaseUri);
|
||||
apiSession = session;
|
||||
var result = await session.LoginAsync(requestedId);
|
||||
if (generation != apiLoginGeneration || requestedId != CustomId)
|
||||
return false;
|
||||
if (!result.Success)
|
||||
{
|
||||
Debug.LogWarning($"API login failed: {result.Error}, HTTP {result.StatusCode}");
|
||||
return false;
|
||||
}
|
||||
customServerMgr.UseAccountSession(session);
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) return true; // Identity is published only after all KV modules load.
|
||||
#endif
|
||||
InternalId = session.InternalId;
|
||||
GContext.Publish(new EvtAPISvrLoginSuccess
|
||||
{
|
||||
playerId = session.PlayerId,
|
||||
internalId = InternalId
|
||||
});
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (generation == apiLoginGeneration)
|
||||
{
|
||||
InternalId = 0;
|
||||
apiSession?.Dispose();
|
||||
apiSession = null;
|
||||
customServerMgr.ClearAccountSession();
|
||||
}
|
||||
Debug.LogError($"API login failed: {e.GetType().Name}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async virtual Task<(int code, int userid)> LinkAccount(EAccoutType accoutType)
|
||||
{
|
||||
await Awaiters.NextFrame;
|
||||
//测试
|
||||
//#if UNITY_EDITOR
|
||||
// var _accountInfo = new TYSdkFacade.AccountInfo()
|
||||
// {
|
||||
// channel = accoutType,
|
||||
// avatar = "https://tse2-mm.cn.bing.net/th/id/OIP-C.s4x0YU7qC-oat3mnyubG5gAAAA?rs=1&pid=ImgDetMain",
|
||||
// };
|
||||
// AvatarToPF(_accountInfo);
|
||||
|
||||
// if (_accountInfo.channel == EAccoutType.hwFacebook)
|
||||
// {
|
||||
// _ = UpdateAvatarUrl(_accountInfo.avatar);
|
||||
// }
|
||||
//#endif
|
||||
return (1,0);
|
||||
}
|
||||
|
||||
protected void AvatarToPF(TYSdkFacade.AccountInfo accountInfo)
|
||||
{
|
||||
if (accountInfo != null
|
||||
&& (accountInfo.channel == EAccoutType.hwFacebook
|
||||
|| accountInfo.channel == EAccoutType.hwGoogle)
|
||||
&& !string.IsNullOrEmpty(accountInfo.avatar))
|
||||
{
|
||||
if (EAccoutUrl == null)
|
||||
{
|
||||
EAccoutUrl = new Dictionary<string, string>();
|
||||
}
|
||||
EAccoutUrl[accountInfo.channel.ToString()] = accountInfo.avatar;
|
||||
string EAccoutUrlStr = Newtonsoft.Json.JsonConvert.SerializeObject(EAccoutUrl);
|
||||
PlayFabMgr.Instance.UpdateUserDataValue("EAccoutUrl", EAccoutUrlStr);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitData(Dictionary<string, UserDataRecord> datas, string userId)
|
||||
{
|
||||
var comparedData = PlayFabMgr.Instance.CompareDatas(datas, userId);
|
||||
if (comparedData != null)
|
||||
{
|
||||
MMTestValue = 0;
|
||||
if (!comparedData.ContainsKey(MMTestKey))
|
||||
{
|
||||
if (config.Get<bool>("NewlyCreated", false))
|
||||
{
|
||||
//新用户设置MMTest
|
||||
MMTestValue = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//老用户设置MMTest
|
||||
PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string mmTestValue = comparedData[MMTestKey];
|
||||
string[] mmTestValueData = mmTestValue.Split('#');
|
||||
//if (mmTestValueData.Length > 1 && mmTestValueData[0] == MMTestSubOldKey)
|
||||
//{
|
||||
// int.TryParse(mmTestValueData[1], out int value);
|
||||
// MMTestValue = value & 0b101;
|
||||
// //升级老用户数据
|
||||
// PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
//}
|
||||
//else
|
||||
if (mmTestValueData.Length > 1 && mmTestValueData[0] == MMTestSubKey)
|
||||
{
|
||||
|
||||
int.TryParse(mmTestValueData[1], out int value);
|
||||
MMTestValue = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
MMTestValue = 0;
|
||||
PlayFabMgr.Instance.UpdateUserDataValue(MMTestKey, $"{MMTestSubKey}#{MMTestValue}");
|
||||
}
|
||||
}
|
||||
GContext.container.Resolve<PlayerData>().LoadPlayerData(comparedData);
|
||||
OnInitData?.Invoke(comparedData);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Logout()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) { _=StopKVGameplayLoopAsync(); return; }
|
||||
#endif
|
||||
if(kvControlledInstance)
|
||||
{
|
||||
logoutGeneration++; InternalId=0; UserId=null; DisplayName=null; AvatarUrl=null;
|
||||
_=kvCoordinator?.Revoke();
|
||||
var previous=loginLifetime; loginLifetime=new System.Threading.CancellationTokenSource();
|
||||
try { previous.Cancel(); } finally { previous.Dispose(); }
|
||||
return;
|
||||
}
|
||||
|
||||
logoutGeneration++;
|
||||
apiLoginGeneration++;
|
||||
apiLoginTask = null;
|
||||
apiSession?.Dispose();
|
||||
apiSession = null;
|
||||
UserId = null;
|
||||
InternalId = 0;
|
||||
DisplayName = null;
|
||||
AvatarUrl = null;
|
||||
PlayFabClientAPI.ForgetAllCredentials();
|
||||
customServerMgr.ClearAccountSession();
|
||||
var previousLifetime = loginLifetime;
|
||||
loginLifetime = new System.Threading.CancellationTokenSource();
|
||||
try { previousLifetime.Cancel(); }
|
||||
finally { previousLifetime.Dispose(); }
|
||||
}
|
||||
|
||||
public virtual async Task<string> ChangeDisplayName(string newName)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
var originName = string.IsNullOrEmpty(DisplayName) ? NewPlayerName : DisplayName;
|
||||
var result = await PlayFabClientAsyncAPI.UpdateUserTitleDisplayNameAsync(new UpdateUserTitleDisplayNameRequest
|
||||
{
|
||||
DisplayName = newName
|
||||
});
|
||||
if (generation != logoutGeneration || result == null)
|
||||
return originName;
|
||||
DisplayName = result.DisplayName;
|
||||
GContext.Publish(new ChangeAvatarEvent());
|
||||
UpdateProfileName();
|
||||
return DisplayName;
|
||||
}
|
||||
|
||||
public virtual async Task<string> UpdateAvatarUrl(string url)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
var originUrl = AvatarUrl;
|
||||
|
||||
Debug.Log("UpdateAvatarUrlAsync originUrl:" + originUrl + " url " + url);
|
||||
|
||||
if (string.IsNullOrEmpty(url)) return originUrl;
|
||||
if (url == originUrl)
|
||||
{
|
||||
return originUrl;
|
||||
}
|
||||
var result = await PlayFabClientAsyncAPI.UpdateAvatarUrlAsync(new UpdateAvatarUrlRequest
|
||||
{
|
||||
ImageUrl = url
|
||||
});
|
||||
Debug.Log("UpdateAvatarUrlAsync :" + result);
|
||||
if (generation != logoutGeneration || result == null) return originUrl;
|
||||
AvatarUrl = url;
|
||||
GContext.Publish(new ChangeAvatarEvent());
|
||||
UpdateProfileAvatar();
|
||||
return url;
|
||||
}
|
||||
|
||||
public virtual async Task<string> UpdateLanguage(string code)
|
||||
{
|
||||
var generation = logoutGeneration;
|
||||
if (Language == code)
|
||||
{
|
||||
return Language;
|
||||
}
|
||||
PlayFab.ProfilesModels.SetProfileLanguageRequest setProfileLanguageRequest = new PlayFab.ProfilesModels.SetProfileLanguageRequest()
|
||||
{
|
||||
Language = code.Replace('_', '-')
|
||||
};
|
||||
|
||||
var result = await PlayFabProfilesAsyncAPI.SetProfileLanguageAsync(setProfileLanguageRequest);
|
||||
if (generation == logoutGeneration && result != null)
|
||||
Language = code;
|
||||
return Language;
|
||||
}
|
||||
|
||||
public double CreateTotalSeconds()
|
||||
{
|
||||
return (ZZTimeHelper.UtcNow() - CreateTime).TotalSeconds;
|
||||
}
|
||||
|
||||
public virtual async Task<bool> GetLinkState(EAccoutType accoutType)
|
||||
{
|
||||
await Awaiters.NextFrame;
|
||||
return false;
|
||||
}
|
||||
public void UpdateProfile()
|
||||
{
|
||||
if (!IsAuthorized) return;
|
||||
PlayerProfile playerProfile = new PlayerProfile();
|
||||
playerProfile.Avatar = AvatarUrl;
|
||||
playerProfile.DisplayName = DisplayName;
|
||||
playerProfile.Level = GContext.container.Resolve<PlayerData>().lv;
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
public void UpdateProfileLv()
|
||||
{
|
||||
if (!IsAuthorized) return;
|
||||
PlayerProfile playerProfile = new PlayerProfile();
|
||||
playerProfile.Level = GContext.container.Resolve<PlayerData>().lv;
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
void UpdateProfileName()
|
||||
{
|
||||
if (!IsAuthorized) return;
|
||||
PlayerProfile playerProfile = new PlayerProfile();
|
||||
playerProfile.DisplayName = DisplayName;
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
void UpdateProfileAvatar()
|
||||
{
|
||||
if (!IsAuthorized) return;
|
||||
PlayerProfile playerProfile = new PlayerProfile();
|
||||
playerProfile.Avatar = AvatarUrl;
|
||||
LoginRequest loginRequest = new LoginRequest() { playerId = ApiPlayerId, playerProfile = playerProfile };
|
||||
customServerMgr.AccountRequest("UpdateProfile", loginRequest);
|
||||
}
|
||||
#region 其他玩家头像昵称缓存
|
||||
|
||||
Dictionary<string, PlayInfo> PlayInfos = new Dictionary<string, PlayInfo>();
|
||||
Dictionary<string, int> PlayLvs = new Dictionary<string, int>();
|
||||
public async Task<(string, int)> GetPlayLv(string playFabId)
|
||||
{
|
||||
if (!PlayLvs.ContainsKey(playFabId))
|
||||
{
|
||||
var resultUserData = await PlayFabClientAsyncAPI.GetUserDataAsync(new GetUserDataRequest()
|
||||
{
|
||||
PlayFabId = playFabId,
|
||||
Keys = new List<string>() { "Lv" }
|
||||
});
|
||||
if (resultUserData != null)
|
||||
{
|
||||
string level = resultUserData.Data.TryGetValue("Lv", out var value) ? value.Value : "1";
|
||||
PlayLvs[playFabId] = int.Parse(level);
|
||||
}
|
||||
}
|
||||
int lv = PlayLvs[playFabId];
|
||||
|
||||
return (playFabId, lv);
|
||||
}
|
||||
public int GetPlayerLv(string playFabId)
|
||||
{
|
||||
return PlayLvs.ContainsKey(playFabId) ? PlayLvs[playFabId] : 1;
|
||||
}
|
||||
|
||||
public void SetPlayerLv(string playFabId, int lv)
|
||||
{
|
||||
if (playFabId == UserId)
|
||||
{
|
||||
lv = GContext.container.Resolve<PlayerData>().lv;
|
||||
}
|
||||
PlayLvs[playFabId] = lv;
|
||||
}
|
||||
|
||||
public void ClearPlayInfos()
|
||||
{
|
||||
PlayInfos.Clear();
|
||||
PlayLvs.Clear();
|
||||
}
|
||||
|
||||
public PlayInfo GetPlayInfo(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!PlayInfos.ContainsKey(id))
|
||||
{
|
||||
PlayInfo playInfo = new PlayInfo();
|
||||
playInfo.name = GetDefaultName(id);
|
||||
PlayInfos.Add(id, playInfo);
|
||||
SetPlayInfo(id, playInfo);
|
||||
}
|
||||
return PlayInfos[id];
|
||||
}
|
||||
|
||||
async void SetPlayInfo(string playFabId, PlayInfo playInfo)
|
||||
{
|
||||
if (playFabId == UserId)
|
||||
{
|
||||
playInfo.name = DisplayName;
|
||||
playInfo.avatarUrl = AvatarUrl;
|
||||
GContext.Publish(new GetClubPlayInfoEvent() { id = playFabId, name = playInfo.name, avatarUrl = playInfo.avatarUrl });
|
||||
return;
|
||||
}
|
||||
var resultUserTitle = await PlayFabClientAsyncAPI.GetPlayerProfileAsync(new GetPlayerProfileRequest()
|
||||
{
|
||||
PlayFabId = playFabId,
|
||||
ProfileConstraints = new PlayerProfileViewConstraints()
|
||||
{
|
||||
ShowDisplayName = true,
|
||||
ShowLocations = true,
|
||||
ShowAvatarUrl = true,
|
||||
}
|
||||
});
|
||||
if (resultUserTitle?.PlayerProfile != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(resultUserTitle?.PlayerProfile.DisplayName))
|
||||
{
|
||||
playInfo.name = resultUserTitle?.PlayerProfile.DisplayName;
|
||||
}
|
||||
playInfo.avatarUrl = resultUserTitle?.PlayerProfile.AvatarUrl;
|
||||
GContext.Publish(new GetClubPlayInfoEvent() { id = playFabId, name = playInfo.name, avatarUrl = playInfo.avatarUrl });
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPlayInfo(string playFabId, string name, string avatarUrl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
name = GetDefaultName(playFabId);
|
||||
}
|
||||
PlayInfo playInfo = new PlayInfo();
|
||||
playInfo.name = name;
|
||||
playInfo.avatarUrl = avatarUrl;
|
||||
PlayInfos[playFabId] = playInfo;
|
||||
}
|
||||
#endregion 其他玩家头像昵称缓存
|
||||
}
|
||||
|
||||
public class PlayInfo
|
||||
{
|
||||
public string name;
|
||||
public string avatarUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,648 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using DataCenter;
|
||||
using Game;
|
||||
using GameCore;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Activity;
|
||||
using Activity.EventSubscriber;
|
||||
using SideGameFromFishing;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using Activity.Services;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public struct RewardPanelClose
|
||||
{
|
||||
|
||||
}
|
||||
public struct RewardPanelCloseSkipFace
|
||||
{
|
||||
|
||||
}
|
||||
public struct TargetAddData
|
||||
{
|
||||
public TargetAddData(int id, int curCount, int addCount, bool fly = true)
|
||||
{
|
||||
this.id = id;
|
||||
this.curCount = curCount;
|
||||
this.addCount = addCount;
|
||||
this.fly = fly;
|
||||
}
|
||||
public int id;
|
||||
public int curCount;
|
||||
public int addCount;
|
||||
public bool fly;
|
||||
}
|
||||
public struct ShowData
|
||||
{
|
||||
public ShowData(List<ItemData> itemDatas, RewardType rewardType = RewardType.Normal, int id = 0)
|
||||
{
|
||||
this.itemDatas = itemDatas;
|
||||
this.rewardType = rewardType;
|
||||
this.id = id;
|
||||
textValue = "";
|
||||
}
|
||||
public ShowData(ItemData itemData)
|
||||
{
|
||||
this.itemDatas = new List<ItemData>
|
||||
{
|
||||
itemData
|
||||
};
|
||||
this.rewardType = RewardType.Normal;
|
||||
this.id = 0;
|
||||
textValue = "";
|
||||
}
|
||||
public ShowData(RewardType rewardType = RewardType.Open, int id = 0, string textValue = "")
|
||||
{
|
||||
this.itemDatas = null;
|
||||
this.rewardType = rewardType;
|
||||
//弹窗相应-1数量中断
|
||||
this.id = id;
|
||||
this.textValue = textValue;
|
||||
}
|
||||
public List<ItemData> itemDatas;
|
||||
public RewardType rewardType;
|
||||
public int id;
|
||||
public string textValue;
|
||||
}
|
||||
public class CurRewardQCount
|
||||
{
|
||||
public int count;
|
||||
}
|
||||
public enum RewardType
|
||||
{
|
||||
Open,
|
||||
Normal,
|
||||
CardHolder,
|
||||
SkillUpgrade,
|
||||
CollectionAlbum,
|
||||
FeaturesUnlock,
|
||||
BenefitUpgrade,
|
||||
Destroy,
|
||||
NormalOne,
|
||||
NormalRestore,
|
||||
|
||||
FishBoxOpen,
|
||||
NewRod,
|
||||
AutoOpen,
|
||||
|
||||
FishBoxFly,
|
||||
FishBoxFlyRestore,
|
||||
|
||||
FishCardBoxOpen,
|
||||
}
|
||||
public class UnloadActToNextAct
|
||||
{
|
||||
public UnloadActToNextAct(string actId = "FishingAct", UIType transitionPanel = null, float time = 0.5f)
|
||||
{
|
||||
this.actId = actId;
|
||||
if (transitionPanel == null)
|
||||
{
|
||||
TransitionPanel = UITypes.CloudTransitionPanel;
|
||||
}
|
||||
else
|
||||
{
|
||||
TransitionPanel = transitionPanel;
|
||||
}
|
||||
|
||||
this.time = time;
|
||||
}
|
||||
public string actId;
|
||||
public UIType TransitionPanel = UITypes.CloudTransitionPanel;
|
||||
public float time = 0.5f;
|
||||
}
|
||||
public class FishingStage : AStage
|
||||
{
|
||||
public List<TargetAddData> q_TargetAddData = new List<TargetAddData>();
|
||||
public List<TargetAddData> q_TargetShowData = new List<TargetAddData>();
|
||||
[Inject]
|
||||
public IUserService UserService { get; set; }
|
||||
|
||||
Queue<ShowData> itemDatasQueue = new Queue<ShowData>();
|
||||
ShowData fishBoxOpen = new ShowData();
|
||||
bool fishCardOpen = false;
|
||||
//Queue<ShowData> itemDatasQueueNext = new Queue<ShowData>();
|
||||
|
||||
Dictionary<string, Queue<ShowData>> itemDatasQueueNextDic = new Dictionary<string, Queue<ShowData>>();
|
||||
|
||||
public bool IsNextQueue = false;
|
||||
public string NextQueueKey;
|
||||
CompositeDisposable disposable = new CompositeDisposable();
|
||||
string actName = "";
|
||||
FishingStageData stageData;
|
||||
bool rewardShowing = false;
|
||||
|
||||
private AutoRestart _autoRestart;
|
||||
private bool stopping;
|
||||
private PlayerDataStorage.IPlayerSaveStore sessionSaveStore;
|
||||
|
||||
public override async Task StartAsync()
|
||||
{
|
||||
if (stopping || this == null) return;
|
||||
var user = UserService;
|
||||
var loginLifetime = user.LoginLifetime;
|
||||
sessionSaveStore = GContext.container.Resolve<PlayerDataStorage.IPlayerSaveStore>();
|
||||
var store = sessionSaveStore;
|
||||
var kvUser = user as KVUserService;
|
||||
if (kvUser != null && (!kvUser.IsAuthorized || !kvUser.KVHandoffReady || !store.IsCurrent))
|
||||
throw new System.InvalidOperationException("KV first scene requires the current completed login.");
|
||||
LoadingScreen.SetProgress(0.54f);
|
||||
await Awaiters.NextFrame;
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
|
||||
// wait authorize , 5 sec max
|
||||
var checkAuthorTimes = 0;
|
||||
while (checkAuthorTimes < 10 && !UserService.IsAuthorized)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
checkAuthorTimes++;
|
||||
}
|
||||
|
||||
if (!UserService.IsAuthorized)
|
||||
{
|
||||
Debug.LogWarning("[FishingStage::startAsync] Api Svr not author");
|
||||
}
|
||||
|
||||
if (GContext.container.Resolve<IConfig>().Get("DEBUG", "0") == "1")
|
||||
{
|
||||
var console = await UnityEngine.AddressableAssets.Addressables.InstantiateAsync("IngameDebugConsole").Task;
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested)
|
||||
{
|
||||
if (console != null) UnityEngine.AddressableAssets.Addressables.ReleaseInstance(console);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await GContext.container.Resolve<SoundMgr>().Init((progress) =>
|
||||
{
|
||||
if (!stopping && this != null && !loginLifetime.IsCancellationRequested) LoadingScreen.SetProgress(0.55f + progress * 0.2f);
|
||||
});
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
//GContext.Publish()
|
||||
if (kvUser != null && (!kvUser.IsAuthorized || !kvUser.KVHandoffReady || !store.IsCurrent))
|
||||
throw new System.InvalidOperationException("KV session changed before first scene initialization.");
|
||||
stageData = new FishingStageData();
|
||||
|
||||
LoadingScreen.SetProgress(0.75f);
|
||||
|
||||
// 注册 EventTokenService 为单例,供活动代币相关逻辑统一使用
|
||||
GContext.container.Register<IEventTokenService,EventTokenService>().AsSingleton();
|
||||
GContext.container.Register<IDeferredRewardStashService, DeferredRewardStashService>().AsSingleton();
|
||||
GContext.container.Resolve<IDeferredRewardStashService>().Reset();
|
||||
|
||||
InitData();
|
||||
InitManager();
|
||||
if (kvUser != null)
|
||||
{
|
||||
if (!GContext.container.Resolve<TimeManager.TimeRefreshManager>().InitialRefreshSucceeded)
|
||||
throw new System.InvalidOperationException("KV initial daily refresh failed; previous login must be retained.");
|
||||
// Daily refresh consumes the previous login first. Save its actual player aggregate
|
||||
// and pending module writes before advancing the persisted login timestamp.
|
||||
GContext.container.Resolve<PlayerData>().SavePlayerData();
|
||||
var initialization = await store.FlushAsync(loginLifetime);
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
if (!kvUser.IsAuthorized || !store.IsCurrent || !initialization.Confirmed)
|
||||
throw new System.InvalidOperationException("KV initialization saves were not confirmed.");
|
||||
var loginSaved = await store.WriteAndConfirmAsync("LastLoginTimeUtc",
|
||||
kvUser.LoginTime.ToUniversalTime().ToString("O", System.Globalization.CultureInfo.InvariantCulture), loginLifetime);
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
if (!kvUser.IsAuthorized || !store.IsCurrent || !loginSaved.Confirmed)
|
||||
throw new System.InvalidOperationException("KV login timestamp was not confirmed.");
|
||||
}
|
||||
GContext.container.RegisterInstance<IStageData>(stageData);
|
||||
GContext.container.Register<SurveyService>().AsSingleton();
|
||||
GContext.OnEvent<TargetAddData>().Subscribe(OnAddTarget).AddTo(disposable);
|
||||
GContext.OnEvent<ShowData>().Subscribe(OnShowDataEvent).AddTo(disposable);
|
||||
GContext.OnEvent<ItemClick>().Subscribe(OnItemClickSettlementEvent).AddTo(disposable);
|
||||
GContext.OnEvent<CurRewardQCount>().Subscribe(GetShowDataCount).AddTo(disposable);
|
||||
GContext.OnEvent<UnloadActToNextAct>().Subscribe(ToFishingStageAct).AddTo(disposable);
|
||||
GContext.OnEvent<game.EvtAPISvrUnauthorized>().Subscribe(OnUnauthorized).AddTo(disposable);
|
||||
await GContext.container.Resolve<IUIService>().LoadAtlas();
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
LoadingScreen.SetProgress(0.77f);
|
||||
|
||||
//这里拿补单信息,拿到之后再进入主界面处理
|
||||
await GContext.container.Resolve<PlayerShopData>().GetReplenishmentData();
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
//加载当前场景
|
||||
var mapData = playerData.GetCurrentMapData();
|
||||
ILoadResourceService loadResourceService = GContext.container.Resolve<ILoadResourceService>();
|
||||
List<string> mapNames = new List<string>() { mapData.EnvName };
|
||||
mapNames.AddRange(GContext.container.Resolve<CampDataMM>().AllPrefabs);
|
||||
bool result = await loadResourceService.Loading(mapNames, (progress) =>
|
||||
{
|
||||
if (!stopping && this != null && !loginLifetime.IsCancellationRequested) LoadingScreen.SetProgress(0.77f + progress * 0.13f);
|
||||
});
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
if (!result)
|
||||
{
|
||||
playerData.SetCurrentMapId(GContext.container.Resolve<Tables>().TbMapData.DataList[0].ID);
|
||||
}
|
||||
LoadingScreen.SetLoadingInfo(LocalizationMgr.GetText("msg_loadingfinishing"));
|
||||
|
||||
GContext.container.Resolve<LeadboardData>().SetLvData();
|
||||
try { await base.StartAsync(); }
|
||||
catch (System.Exception ex) when (stopping || this == null || loginLifetime.IsCancellationRequested)
|
||||
{
|
||||
Debug.LogWarning($"[FishingStage] Startup stopped, cancelled or stage destroyed: {ex.GetType().Name}: {ex.Message}");
|
||||
return;
|
||||
}
|
||||
if (stopping || this == null || loginLifetime.IsCancellationRequested) return;
|
||||
//静默加载后续场景
|
||||
playerData.LoadNewMap();
|
||||
//更新数据
|
||||
//GContext.container.Resolve<LeadboardData>().UpdateMemberRank();
|
||||
LoadingScreen.SetProgress(1);
|
||||
GContext.container.Resolve<SurveyService>().GetSurveyMatchlist();
|
||||
AiHelpManager.Instance.Login();
|
||||
//AntiAddiction
|
||||
// await GContext.container.Resolve<AntiAddictionService>().ChckIfCanPlay();
|
||||
// GContext.container.Register<IDeferredRewardStashService, DeferredRewardStashService>().AsSingleton();
|
||||
//
|
||||
InitEvent();
|
||||
GContext.container.Resolve<AntiCheatManager>().CheckBan();
|
||||
_autoRestart = new AutoRestart();
|
||||
_autoRestart.Init();
|
||||
}
|
||||
|
||||
void InitEvent()
|
||||
{
|
||||
var rtService = GContext.container.Resolve<IRTService>();
|
||||
rtService.OnEvent<NotifyFriendInvitedCount>(notify => GContext.container.Resolve<FriendService>().GetInvitedListCount())
|
||||
.AddTo(disposable);
|
||||
|
||||
GContext.container.Resolve<FriendService>().GetInvitedListCount();
|
||||
|
||||
MainThreadDispatcher.OnApplicationPauseAsObservable()
|
||||
.Subscribe(paused =>
|
||||
{
|
||||
if (paused) _ = FlushPausedSaveAsync();
|
||||
else _ = OnApplicationResumedAsync();
|
||||
})
|
||||
.AddTo(disposable);
|
||||
}
|
||||
|
||||
private async Task FlushPausedSaveAsync()
|
||||
{
|
||||
var user = UserService;
|
||||
var store = sessionSaveStore;
|
||||
if (stopping || user is not KVUserService || !user.IsAuthorized || store == null || !store.IsCurrent) return;
|
||||
try
|
||||
{
|
||||
var result = await store.FlushAsync(user.LoginLifetime);
|
||||
if (!stopping && store.IsCurrent && !result.Confirmed)
|
||||
Debug.LogWarning("KV pause save: " + result.Status);
|
||||
}
|
||||
catch (System.Exception ex) { Debug.LogError("KV pause save failed: " + ex.GetType().Name); }
|
||||
}
|
||||
|
||||
private async Task OnApplicationResumedAsync()
|
||||
{
|
||||
var userService = UserService;
|
||||
if (userService == null || !userService.IsLogin)
|
||||
return;
|
||||
var lifetime = userService.LoginLifetime;
|
||||
var store = sessionSaveStore;
|
||||
if (userService is KVUserService && (!userService.IsAuthorized || store == null || !store.IsCurrent)) return;
|
||||
|
||||
var rtService = GContext.container.Resolve<IRTService>();
|
||||
var mailService = GContext.container.Resolve<IInGameMailService>();
|
||||
if (rtService != null && mailService != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await rtService.Connect();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
if (stopping || lifetime.IsCancellationRequested || !userService.IsLogin ||
|
||||
(userService is KVUserService && !store.IsCurrent)) return;
|
||||
await mailService.FetchMail();
|
||||
}
|
||||
}
|
||||
|
||||
void InitData()
|
||||
{
|
||||
GContext.container.Resolve<CampDataMM>().Init();
|
||||
GContext.container.Resolve<PlayerData>().Init();
|
||||
GContext.container.Resolve<FishingEventData>().Init();
|
||||
GContext.container.Resolve<PlayerFishData>().Init();
|
||||
GContext.container.Resolve<GuideDataCenter>().Init();
|
||||
GContext.container.Resolve<PlayerShopData>().Init();
|
||||
GContext.container.Resolve<TriggerPackData>().Init();
|
||||
//- 不要再这里Init,保持原来的数据
|
||||
// GContext.container.Resolve<OfferChainsChestManager>().Init();
|
||||
// GContext.container.Resolve<OfferDiscoPartyManager>().Init();
|
||||
GContext.container.Resolve<EventPartnerGatherManager>().Init();
|
||||
GContext.container.Resolve<EventCubeMeltManager>().Init();
|
||||
GContext.container.Resolve<FishingChallengeManager>().Init();
|
||||
GContext.container.Resolve<ClubService>().Init();
|
||||
GContext.container.Resolve<AchievementDataManager>().Init();
|
||||
GContext.container.Resolve<SmallGameData>().Init();
|
||||
GContext.Publish(new ConditionTypeEvent { type = cfg.ConditionType.LoginDays, count = 1 });
|
||||
}
|
||||
private void OnAddTarget(TargetAddData data)
|
||||
{
|
||||
if (data.fly)
|
||||
{
|
||||
q_TargetAddData.Add(data);
|
||||
}
|
||||
q_TargetShowData.Add(data);
|
||||
}
|
||||
|
||||
public void ClearTargetAddData()
|
||||
{
|
||||
q_TargetAddData.Clear();
|
||||
q_TargetShowData.Clear();
|
||||
}
|
||||
|
||||
void GetShowDataCount(CurRewardQCount curRewardQCount)
|
||||
{
|
||||
curRewardQCount.count = itemDatasQueue.Count;
|
||||
if (fishCardOpen)
|
||||
{
|
||||
curRewardQCount.count++;
|
||||
}
|
||||
if (fishBoxOpen.id > 0)
|
||||
{
|
||||
curRewardQCount.count++;
|
||||
}
|
||||
}
|
||||
|
||||
void OnShowDataEvent(ShowData data)
|
||||
{
|
||||
if (data.rewardType == RewardType.Open || data.rewardType == RewardType.AutoOpen)
|
||||
{
|
||||
ShowReward(data);
|
||||
}
|
||||
else if (data.rewardType == RewardType.Destroy)
|
||||
{
|
||||
OnDestroyUI(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnAddItem(data);
|
||||
}
|
||||
}
|
||||
|
||||
void OnAddItem(ShowData showData)
|
||||
{
|
||||
if (IsNextQueue)
|
||||
{
|
||||
//if (showData.rewardType == RewardType.FishBoxOpen)
|
||||
//{
|
||||
// if (itemDatasQueueNext.Where((_) => _.rewardType == showData.rewardType).Count() > 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//}
|
||||
//itemDatasQueueNext.Enqueue(showData);
|
||||
if (!itemDatasQueueNextDic.TryGetValue(NextQueueKey, out Queue<ShowData> queue))
|
||||
{
|
||||
queue = new Queue<ShowData>();
|
||||
itemDatasQueueNextDic.Add(NextQueueKey, queue);
|
||||
}
|
||||
queue.Enqueue(showData);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (showData.rewardType == RewardType.FishBoxOpen)
|
||||
{
|
||||
if (fishBoxOpen.id > 0)
|
||||
{
|
||||
showData.itemDatas = null;
|
||||
}
|
||||
fishBoxOpen = showData;
|
||||
}
|
||||
else if (showData.rewardType == RewardType.CardHolder)
|
||||
{
|
||||
fishCardOpen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemDatasQueue.Enqueue(showData);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void NextToQueue(string nextQueueKey)
|
||||
{
|
||||
if (itemDatasQueueNextDic.ContainsKey(nextQueueKey))
|
||||
{
|
||||
var queue = itemDatasQueueNextDic[nextQueueKey];
|
||||
if (queue.Count > 0)
|
||||
{
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
itemDatasQueue.Enqueue(queue.Dequeue());
|
||||
}
|
||||
}
|
||||
itemDatasQueueNextDic.Remove(nextQueueKey);
|
||||
}
|
||||
//if (itemDatasQueueNext.Count > 0)
|
||||
//{
|
||||
// while (itemDatasQueueNext.Count > 0)
|
||||
// {
|
||||
// itemDatasQueue.Enqueue(itemDatasQueueNext.Dequeue());
|
||||
// }
|
||||
//}
|
||||
}
|
||||
ShowData showData;
|
||||
void ShowReward(ShowData showData)
|
||||
{
|
||||
AddBox();
|
||||
if (itemDatasQueue.Count == 0 || rewardShowing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.showData = showData;
|
||||
ShowPeek();
|
||||
}
|
||||
void AddBox()
|
||||
{
|
||||
if (fishCardOpen)
|
||||
{
|
||||
var fishCardShowData = new ShowData(RewardType.CardHolder);
|
||||
itemDatasQueue.Enqueue(fishCardShowData);
|
||||
fishCardOpen = false;
|
||||
}
|
||||
|
||||
if (fishBoxOpen.id > 0)
|
||||
{
|
||||
itemDatasQueue.Enqueue(fishBoxOpen);
|
||||
fishBoxOpen = new ShowData();
|
||||
}
|
||||
}
|
||||
void OnDestroyUI(ShowData showData)
|
||||
{
|
||||
rewardShowing = false;
|
||||
AddBox();
|
||||
GContext.container.Resolve<PlayerData>().ShowNewRod();
|
||||
|
||||
if (itemDatasQueue.Count > 0)
|
||||
{
|
||||
itemDatasQueue.Dequeue();
|
||||
ShowPeek();
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyRewardPanel();
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyRewardPanel()
|
||||
{
|
||||
UIManager.Instance.DestroyUI(UITypes.RewardPopupPanel);
|
||||
GContext.Publish(new RewardPanelClose());
|
||||
if (itemClick != null)
|
||||
{
|
||||
GContext.Publish<ItemClick>(itemClick);
|
||||
}
|
||||
}
|
||||
|
||||
async void ShowPeek()
|
||||
{
|
||||
if (itemDatasQueue.Count == 0 || this.showData.id == 1)
|
||||
{
|
||||
rewardShowing = false;
|
||||
DestroyRewardPanel();
|
||||
}
|
||||
else
|
||||
{
|
||||
rewardShowing = true;
|
||||
this.showData.id--;
|
||||
ShowData curShowData = itemDatasQueue.Peek();
|
||||
RewardPopupPanel rewardPopupPanel = (await UIManager.Instance.ShowUI(UITypes.RewardPopupPanel)).GetComponent<RewardPopupPanel>();
|
||||
rewardPopupPanel.ShowPanel(curShowData, showData.textValue, showData.rewardType);
|
||||
}
|
||||
}
|
||||
private ItemClick itemClick;
|
||||
private async void OnItemClickSettlementEvent(ItemClick data)
|
||||
{
|
||||
if (data == null)
|
||||
return;
|
||||
//缓存数据 等待奖励界面关闭,触发
|
||||
if (itemClick == null)
|
||||
{
|
||||
itemClick = data;
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(data.Prefab))
|
||||
{
|
||||
itemClick = null;
|
||||
return;
|
||||
}
|
||||
UITypes.ItemClickSettlementPanel.SetType(data.Prefab);
|
||||
var panel = (await UIManager.Instance.ShowUILoad(UITypes.ItemClickSettlementPanel))
|
||||
?.GetComponent<ItemClickSettlementPanel>();
|
||||
panel?.SetData(data);
|
||||
itemClick = null;
|
||||
}
|
||||
void ToFishingStageAct(UnloadActToNextAct toFishingStageAct)
|
||||
{
|
||||
if (actName == toFishingStageAct.actId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var faceService = GContext.container.Resolve<IFaceUIService>();
|
||||
faceService.StopFaceUI();
|
||||
faceService.StopPostFaceUI();
|
||||
UIManager.Instance.panelStack.Clear();
|
||||
actName = toFishingStageAct.actId;
|
||||
_transitionPanel = toFishingStageAct.TransitionPanel;
|
||||
transitionTime = toFishingStageAct.time;
|
||||
CloudTransitionPanel();
|
||||
}
|
||||
|
||||
async void CloudTransitionPanel()
|
||||
{
|
||||
var transitionPanel = _transitionPanel ?? UITypes.CloudTransitionPanel;
|
||||
await UIManager.Instance.ShowUI(transitionPanel);
|
||||
await Awaiters.Seconds(transitionTime);
|
||||
UIManager.Instance.DestroyUI(UITypes.LackOfResourcePopupPanel);
|
||||
await LoadActAsync(actName);
|
||||
}
|
||||
|
||||
private async void OnUnauthorized(EvtAPISvrUnauthorized evt)
|
||||
{
|
||||
var userService = GContext.container.Resolve<IUserService>();
|
||||
// if (userService.IsAuthorized)
|
||||
// {
|
||||
// GameObject go = await UIManager.Instance.ShowUI(UITypes.NoticeConfirmPopupPanel);
|
||||
// var noticeConfirmPopupPanel = go.GetComponent<NoticeConfirmPopupPanel>();
|
||||
// string title = LocalizationMgr.GetText("UI_NoticePopupPanel_3");
|
||||
// string info = LocalizationMgr.GetText("UI_NoticePopupPanel_4");
|
||||
// string label = LocalizationMgr.GetText("UI_COMMON_confirm");
|
||||
// var stageService = GContext.container.Resolve<IStageService>();
|
||||
// noticeConfirmPopupPanel.Init(1, title, info,
|
||||
// onClickLeft: () => { stageService.SwitchStageAsync("QuitStage"); },
|
||||
// onClose: () => { stageService.SwitchStageAsync("QuitStage"); },
|
||||
// btn_left_text: label);
|
||||
// }
|
||||
}
|
||||
|
||||
public override async Task Stop()
|
||||
{
|
||||
stopping = true;
|
||||
GContext.container.Unregister<IStageData>();
|
||||
GContext.container.Unregister<SurveyService>();
|
||||
GContext.container.Resolve<IDeferredRewardStashService>()?.DisposeSubscriptions();
|
||||
GContext.container.Unregister<IDeferredRewardStashService>();
|
||||
disposable?.Dispose();
|
||||
disposable = null;
|
||||
_autoRestart?.Dispose();
|
||||
_autoRestart = null;
|
||||
await base.Stop();
|
||||
}
|
||||
protected override void OnDestroy() { stopping = true; }
|
||||
|
||||
//protected override async Task OnActUnload(IAct act, bool requestNextAct)
|
||||
//{
|
||||
// if (requestNextAct)
|
||||
// {
|
||||
// var transitionPanel = _transitionPanel ?? UITypes.CloudTransitionPanel;
|
||||
// await UIManager.Instance.ShowUI(transitionPanel);
|
||||
// await Task.Delay(500);
|
||||
// UIManager.Instance.DestroyUI(UITypes.LackOfResourcePopupPanel);
|
||||
// }
|
||||
// await base.OnActUnload(act, requestNextAct);
|
||||
//}
|
||||
|
||||
private UIType _transitionPanel;
|
||||
float transitionTime = 0.5f;
|
||||
protected override async Task OnActLoaded(IAct act)
|
||||
{
|
||||
GContext.Publish(new EndTransition());
|
||||
await base.OnActLoaded(act);
|
||||
await Awaiters.SecondsRealtime(0.5f);
|
||||
LoadingScreen.Hide();
|
||||
}
|
||||
|
||||
protected override async Task RequestNextAct(string actId)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
//await LoadActAsync(actName);
|
||||
}
|
||||
|
||||
//init by type
|
||||
public void InitManager()
|
||||
{
|
||||
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
|
||||
{
|
||||
OrderManager.OrderManager.Init(type);
|
||||
SideGameFromFishingManager.Init(type);
|
||||
ActivitySentryManager.InitActivitySentryType(type);
|
||||
ActivitySubscriberManager.InitEventSubscriber(type);
|
||||
}
|
||||
GContext.container.Resolve<ActivityManager>().Init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
using asap.core;
|
||||
using DataCenter;
|
||||
using Game;
|
||||
using GameCore;
|
||||
using Script.RuntimeScript;
|
||||
using System.Threading.Tasks;
|
||||
using TimeManager;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class LoginStage : AStage
|
||||
{
|
||||
protected override void OnDestroy() { }
|
||||
|
||||
|
||||
public override async Task StartAsync()
|
||||
{
|
||||
using (GContext.container.InjectionScope())
|
||||
{
|
||||
#if AGG
|
||||
GContext.container.Register<IIAPService, AggIAPService>().AsSingleton();
|
||||
GContext.container.Register<IUserService, AggUserService>().AsSingleton();
|
||||
GContext.container.Register<PlayerDataStorage.IPlayerSaveStore, PlayerDataStorage.PlayFabPlayerSaveStore>().AsSingleton();
|
||||
#else
|
||||
if (SubBoostrap.RuntimeSelection?.IsKV == true)
|
||||
{
|
||||
if (PlayFabMgr.HasLiveInstanceForKVLoad)
|
||||
throw new System.InvalidOperationException("KV startup found an active legacy save manager.");
|
||||
GContext.container.Register<IUserService, KVUserService>().AsSingleton();
|
||||
GContext.container.Register<PlayerDataStorage.IPlayerSaveStore, PlayerDataStorage.KVPlayerSaveStore>().AsSingleton();
|
||||
}
|
||||
else
|
||||
{
|
||||
GContext.container.Register<IUserService, PlayFabUserService>().AsSingleton();
|
||||
GContext.container.Register<PlayerDataStorage.IPlayerSaveStore, PlayerDataStorage.PlayFabPlayerSaveStore>().AsSingleton();
|
||||
}
|
||||
GContext.container.Register<IIAPService, DevIAPService>().AsSingleton();
|
||||
#endif
|
||||
GContext.container.Register<PlayerFishData>().AsSingleton();
|
||||
GContext.container.Register<PlayerItemData>().AsSingleton();
|
||||
GContext.container.Register<CampDataMM>().AsSingleton();
|
||||
GContext.container.Register<FishingEventData>().AsSingleton();
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(PlayerDataStorage.PlayerKVGameplayLoop.Enabled || SubBoostrap.RuntimeSelection?.IsKV==true)
|
||||
GContext.container.Register<EventShootingRangeData>().AsSingleton();
|
||||
#else
|
||||
if(SubBoostrap.RuntimeSelection?.IsKV==true)
|
||||
GContext.container.Register<EventShootingRangeData>().AsSingleton();
|
||||
#endif
|
||||
GContext.container.Register<SigninData>().AsSingleton();
|
||||
//GContext.container.Register<TaskData>().AsSingleton();
|
||||
GContext.container.Register<ClubService>().AsSingleton();
|
||||
GContext.container.Register<LeadboardData>().AsSingleton();
|
||||
GContext.container.Register<PlayerShopData>().AsSingleton();
|
||||
GContext.container.Register<TriggerPackData>().AsSingleton();
|
||||
GContext.container.Register<GuideDataCenter>().AsSingleton();
|
||||
GContext.container.Register<SmallGameData>().AsSingleton();
|
||||
GContext.container.Register<PlayerData>().AsSingleton();
|
||||
GContext.container.Register<MMTService>().AsSingleton();
|
||||
GContext.container.Register<BuffDataCenter>().AsSingleton();
|
||||
GContext.container.Register<FishingBoxData>().AsSingleton();
|
||||
GContext.container.Register<FishingBoxDataProvier>().AsSingleton();
|
||||
// GContext.container.Register<FishingChallengeCenter>().AsSingleton();
|
||||
GContext.container.Register<FishingChallengeManager>().AsSingleton();
|
||||
GContext.container.Register<OfferChainsChestManager>().AsSingleton();
|
||||
GContext.container.Register<OfferDiscoPartyManager>().AsSingleton();
|
||||
GContext.container.Register<BattlePassDataProvider>().AsSingleton();
|
||||
GContext.container.Register<MiniBattlePassDataProvider>().AsSingleton();
|
||||
// GContext.container.Register<OfferRushDataProvider>().AsSingleton();
|
||||
GContext.container.Register<BattlePassData>().AsSingleton();
|
||||
GContext.container.Register<EventPackData>().AsSingleton();
|
||||
GContext.container.Register<AchievementDataManager>().AsSingleton();
|
||||
|
||||
//GContext.container.Register<PokerScoreData>().AsSingleton();
|
||||
GContext.container.Register<PiggyBankPackData>().AsSingleton();
|
||||
|
||||
GContext.container.Register<BargainPackData>().AsSingleton();
|
||||
GContext.container.Register<DiggingGameManager>().AsSingleton();
|
||||
GContext.container.Register<EventPartnerData>().AsSingleton();
|
||||
GContext.container.Register<EventPartnerGatherManager>().AsSingleton();
|
||||
GContext.container.Register<EventCubeMeltManager>().AsSingleton();
|
||||
GContext.container.Register<IFootPrintService, FootPrintService>().AsSingleton();
|
||||
GContext.container.Register<FriendService>().AsSingleton();
|
||||
GContext.container.Register<EventPartnerService>().AsSingleton();
|
||||
GContext.container.Register<EventPotionDataManager>().AsSingleton();
|
||||
GContext.container.Register<EventPinballDataManager>().AsSingleton();
|
||||
GContext.container.Register<EventPinataPackDataManager>().AsSingleton();
|
||||
GContext.container.Register<EventScratchTicketDataManager>().AsSingleton();
|
||||
GContext.container.Register<Assets.Scripts.UI.OfferSweep.OfferSweepManager>().AsSingleton();
|
||||
GContext.container.Register<Assets.Scripts.UI.AdWheel.AdWheelManager>().AsSingleton();
|
||||
GContext.container.Register<Assets.Scripts.UI.JigsawPuzzle.JigsawPuzzleManager>().AsSingleton();
|
||||
|
||||
GContext.container.Register<AntiCheatManager>().AsSingleton();
|
||||
GContext.container.Register<Activity.ActivityManager>().AsSingleton();
|
||||
GContext.container.Register<Activity.ActivityScopeManager>().AsSingleton();
|
||||
GContext.container.Register<TimeRefreshManager>().AsSingleton();
|
||||
GContext.container.Register<ITimeTickService, TimeTickService>().AsSingleton();
|
||||
|
||||
|
||||
UnityEngine.Debug.Log("LoginStage StartAsync step 2");
|
||||
}
|
||||
|
||||
UnityEngine.Debug.Log("LoginStage StartAsync step 3");
|
||||
|
||||
LoadingScreen.SetProgress(0.20f);
|
||||
#if AGG
|
||||
startActId = "AggLoginAct";
|
||||
#else
|
||||
startActId = "PFLoginAct";
|
||||
#endif
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) startActId="PFLoginAct";
|
||||
#endif
|
||||
if (!LoadingScreen.isShowing())
|
||||
{
|
||||
await LoadingScreen.Show();
|
||||
}
|
||||
LoadingScreen.SetLoadingInfo(Boostrapi18n.Instance.GetText("msg_loggingin"));
|
||||
UnityEngine.Debug.Log($"LoginStage StartAsync {startActId}");
|
||||
|
||||
await base.StartAsync();
|
||||
}
|
||||
|
||||
protected override async Task RequestNextAct(string actId)
|
||||
{
|
||||
if(SubBoostrap.RuntimeSelection?.IsKV==true)
|
||||
return;
|
||||
await GContext.container.Resolve<IStageService>().SwitchStageAsync("FishingStage");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using GameCore;
|
||||
using System.Linq;
|
||||
|
||||
namespace game
|
||||
{
|
||||
|
||||
public class PFLoginAct : AGameAct
|
||||
{
|
||||
[Inject] public IUserService userService { get; set; }
|
||||
[Inject] public IConfig config { get; set; }
|
||||
[Inject] public IInGameMailService MailService { get; set; }
|
||||
[Inject] public IRTService rtService { get; set; }
|
||||
[Inject] public IIAPService iapService { get; set; }
|
||||
|
||||
private PlayerDataStorage.IKVUserLifecycle kvLifecycle => userService as PlayerDataStorage.IKVUserLifecycle;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject customInput;
|
||||
|
||||
[SerializeField]
|
||||
private InputField customIdInput;
|
||||
[SerializeField]
|
||||
private Button inputOkBtn;
|
||||
|
||||
[SerializeField] SimpleDialog dialog;
|
||||
private string[] loadingInfos;
|
||||
private CancellationTokenSource loginLifetime;
|
||||
private CancellationToken loginToken;
|
||||
|
||||
public override async Task<bool> StartAsync()
|
||||
{
|
||||
loginLifetime = CancellationTokenSource.CreateLinkedTokenSource(userService.LoginLifetime);
|
||||
loginToken = loginLifetime.Token;
|
||||
Debug.Log("PFLoginAct StartAsync");
|
||||
|
||||
loadingInfos = new string[] {
|
||||
"UI_FishingLoginPanel_LoginGameStage_1",
|
||||
"UI_FishingLoginPanel_LoginGameStage_2",
|
||||
"UI_FishingLoginPanel_LoginGameStage_3",
|
||||
"UI_FishingLoginPanel_LoginGameStage_4",
|
||||
"UI_FishingLoginPanel_LoginGameStage_5",
|
||||
"UI_FishingLoginPanel_LoginGameStage_6",
|
||||
"UI_FishingLoginPanel_LoginGameStage_7",
|
||||
"UI_FishingLoginPanel_LoginGameStage_8",
|
||||
"UI_FishingLoginPanel_LoginGameStage_9"
|
||||
}.Select(key => LocalizationMgr.GetText(key)).ToArray();
|
||||
|
||||
await base.StartAsync();
|
||||
if (loginToken.IsCancellationRequested) return false;
|
||||
DoLogin();
|
||||
return true;
|
||||
}
|
||||
|
||||
//CancellationTokenSource source;
|
||||
private async void DoLogin()
|
||||
{
|
||||
try { await LoginAsync(); }
|
||||
catch (OperationCanceledException) when (loginToken.IsCancellationRequested) { }
|
||||
catch (Exception ex) { Debug.LogException(ex); }
|
||||
finally { StopLoadingInfo(); }
|
||||
}
|
||||
|
||||
private async Task LoginAsync()
|
||||
{
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
UpdateLoadingInfo();
|
||||
var configCustomId = config.Get<string>("CustomID", string.Empty);
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) configCustomId=game.PlayerDataStorage.PlayerKVGameplayLoop.Permit.PlayerId;
|
||||
#endif
|
||||
if (configCustomId == @"{{input}}")
|
||||
{
|
||||
LoadingScreen.HideProgressBar();
|
||||
StopLoadingInfo();
|
||||
await InputID();
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
LoadingScreen.ShowProgressBar();
|
||||
UpdateLoadingInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
userService.CustomId = configCustomId;
|
||||
}
|
||||
|
||||
//source = new CancellationTokenSource();
|
||||
//LoadingAutoInc(source.Token, 1f);
|
||||
|
||||
var formalKV=SubBoostrap.RuntimeSelection?.IsKV==true;
|
||||
rtService.WaitForAPISvrLogin();
|
||||
var step = await userService.Login(OnLoginStep);
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
if(formalKV)
|
||||
{
|
||||
StopLoadingInfo();
|
||||
LoadingScreen.HideProgressBar();
|
||||
if(step==ELoginStep.Done && kvLifecycle?.KVHandoffReady==true)
|
||||
LoadingScreen.SetLoadingInfo("KV account loaded. Gameplay is not enabled yet.");
|
||||
else
|
||||
{
|
||||
var error=kvLifecycle?.KVLastError.ToString() ?? "Input";
|
||||
dialog.Config(new SimpleDialog.Context
|
||||
{
|
||||
message="KV login unavailable",
|
||||
message2="Login stopped ("+error+"). Sign out or restart before trying again.",
|
||||
okAction=()=>userService.Logout(), closeAction=()=>userService.Logout()
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (step != ELoginStep.Done)
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) throw game.PlayerDataStorage.PlayerKVGameplayLoop.Stop("Login failed; restart the Unity process before another attempt.");
|
||||
#endif
|
||||
//source.Cancel();
|
||||
//source.Dispose();
|
||||
await ReytryDialog(userService.Login, LocalizationMgr.GetText("subtitle_loginfailed")
|
||||
, LocalizationMgr.GetText("msg_loginfailed"));
|
||||
}
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
|
||||
StopLoadingInfo();
|
||||
//source.Cancel();
|
||||
//source.Dispose();
|
||||
// try
|
||||
// {
|
||||
// await rtService.Connect();
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Debug.LogException(ex);
|
||||
// }
|
||||
MailService.Init();
|
||||
iapService.Init();
|
||||
|
||||
Unload();
|
||||
}
|
||||
|
||||
private async Task InputID()
|
||||
{
|
||||
customInput.SetActive(true);
|
||||
var tempInput = PlayerPrefs.GetString("CustomID", string.Empty);
|
||||
var inputTask = new TaskCompletionSource<string>();
|
||||
UnityEngine.Events.UnityAction accept = () =>
|
||||
{
|
||||
if (!loginToken.IsCancellationRequested) inputTask.TrySetResult(customIdInput.text);
|
||||
};
|
||||
inputOkBtn.onClick.AddListener(accept);
|
||||
customIdInput.text = tempInput;
|
||||
try
|
||||
{
|
||||
using (loginToken.Register(() => inputTask.TrySetCanceled()))
|
||||
tempInput = await inputTask.Task;
|
||||
}
|
||||
finally { if (inputOkBtn != null) inputOkBtn.onClick.RemoveListener(accept); }
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
if (!string.IsNullOrEmpty(tempInput))
|
||||
{
|
||||
PlayerPrefs.SetString("CustomID", tempInput);
|
||||
}
|
||||
userService.CustomId = tempInput;
|
||||
|
||||
customInput.SetActive(false);
|
||||
}
|
||||
|
||||
private async Task ReytryDialog(Func<Action<int>, bool, Task<ELoginStep>> func, string title, string message)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
//source = new CancellationTokenSource();
|
||||
var retry = new TaskCompletionSource<ELoginStep>();
|
||||
bool submitted = false;
|
||||
async void Submit()
|
||||
{
|
||||
if (submitted || loginToken.IsCancellationRequested) return;
|
||||
submitted = true;
|
||||
try
|
||||
{
|
||||
if (config.Get<string>("CustomID", string.Empty) == @"{{input}}")
|
||||
{
|
||||
LoadingScreen.HideProgressBar();
|
||||
StopLoadingInfo();
|
||||
await InputID();
|
||||
loginToken.ThrowIfCancellationRequested();
|
||||
LoadingScreen.ShowProgressBar();
|
||||
UpdateLoadingInfo();
|
||||
}
|
||||
retry.TrySetResult(await func(OnLoginStep, true));
|
||||
}
|
||||
catch (Exception ex) { retry.TrySetException(ex); }
|
||||
}
|
||||
using var cancellation = loginToken.Register(() => retry.TrySetCanceled());
|
||||
//LoadingAutoInc(source.Token, 1f);
|
||||
dialog.Config(new SimpleDialog.Context()
|
||||
{
|
||||
message = title,
|
||||
message2 = message,
|
||||
okAction = Submit,
|
||||
closeAction = Submit
|
||||
});
|
||||
var result = await retry.Task;
|
||||
//LoadingAutoInc(source.Token, 1f);
|
||||
await Task.Delay(1000, loginToken);
|
||||
if (result == ELoginStep.Done)
|
||||
return;
|
||||
//else
|
||||
//{
|
||||
//source.Cancel();
|
||||
//source.Dispose();
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLoginStep(int step)
|
||||
{
|
||||
if (loginToken.IsCancellationRequested) return;
|
||||
LoadingScreen.SetProgress(0.2f + step / 2.0f * 0.3f);
|
||||
}
|
||||
|
||||
float lastChangeTime = 0;
|
||||
float lastWaitTime = 0;
|
||||
int lastChangeIndex = 0;
|
||||
float Text_Change_interval = 1.5f;
|
||||
float Text_Wait_Interval = 0.15f;
|
||||
bool isUpdatingLoadingInfo;
|
||||
|
||||
private async void UpdateLoadingInfo()
|
||||
{
|
||||
if (isUpdatingLoadingInfo) return;
|
||||
isUpdatingLoadingInfo = true;
|
||||
var deltaTime = 0f;
|
||||
var info = string.Empty;
|
||||
while (isUpdatingLoadingInfo && !loginToken.IsCancellationRequested)
|
||||
{
|
||||
info = GetTextByDeltaTime(deltaTime, info);
|
||||
LoadingScreen.SetLoadingInfo(info);
|
||||
await Awaiters.NextFrame;
|
||||
deltaTime += Time.deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTextByDeltaTime(float deltaTime, string txt)
|
||||
{
|
||||
if (deltaTime == 0)
|
||||
{
|
||||
lastChangeTime = 0;
|
||||
lastChangeIndex = 0;
|
||||
return loadingInfos[0];
|
||||
}
|
||||
|
||||
var interval = deltaTime - lastChangeTime;
|
||||
|
||||
if (interval > Text_Change_interval)
|
||||
{
|
||||
lastWaitTime = deltaTime;
|
||||
lastChangeTime = deltaTime;
|
||||
lastChangeIndex++;
|
||||
return loadingInfos[lastChangeIndex % loadingInfos.Length];
|
||||
}
|
||||
|
||||
return txt;
|
||||
}
|
||||
|
||||
//private string GetTextByDeltaTime(float deltaTime, string txt)
|
||||
//{
|
||||
//if (deltaTime == 0)
|
||||
//{
|
||||
//lastChangeTime = 0;
|
||||
//lastChangeIndex = 0;
|
||||
//return loadingInfos[0];
|
||||
//}
|
||||
|
||||
//var interval = deltaTime - lastChangeTime;
|
||||
|
||||
//if (interval > Text_Change_interval)
|
||||
//{
|
||||
//lastWaitTime = deltaTime;
|
||||
//lastChangeTime = deltaTime;
|
||||
//lastChangeIndex++;
|
||||
//return loadingInfos[lastChangeIndex % loadingInfos.Length];
|
||||
//}
|
||||
|
||||
//interval = deltaTime - lastWaitTime;
|
||||
//if (interval > Text_Wait_Interval)
|
||||
//{
|
||||
//var info = loadingInfos[lastChangeIndex % loadingInfos.Length];
|
||||
//lastWaitTime = deltaTime;
|
||||
//if(txt.Length - info.Length >= 4)
|
||||
//return info;
|
||||
//return txt + ".";
|
||||
//}
|
||||
//return txt;
|
||||
//}
|
||||
|
||||
private void StopLoadingInfo()
|
||||
{
|
||||
isUpdatingLoadingInfo = false;
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
StopLoadingInfo();
|
||||
loginLifetime?.Cancel();
|
||||
loginLifetime?.Dispose();
|
||||
loginLifetime = null;
|
||||
if(SubBoostrap.RuntimeSelection?.IsKV==true) userService.Logout();
|
||||
}
|
||||
|
||||
|
||||
//protected async void LoadingAutoInc(CancellationToken token, float total)
|
||||
//{
|
||||
//float progress = 0.01f;
|
||||
//string msg = LocalizationMgr.GetRandomText();
|
||||
//LoadingScreen.ShowProgressBar();
|
||||
//LoadingScreen.SetProgress(progress);
|
||||
//LoadingScreen.SetLoadingInfo(msg);
|
||||
//string msg_mem = msg;
|
||||
//try
|
||||
//{
|
||||
//float timer=0;
|
||||
//float innertimer=0;
|
||||
//float innerTimer2 = 0;
|
||||
//while(progress < 1)
|
||||
//{
|
||||
//timer+=(Time.deltaTime+0.1f);
|
||||
//innertimer+=(Time.deltaTime+0.1f);
|
||||
//innerTimer2+=(Time.deltaTime+0.1f);
|
||||
//if (token.IsCancellationRequested)
|
||||
//break;
|
||||
////每0.3秒给原信息后加一个.
|
||||
|
||||
|
||||
//if(innertimer>0.3f){
|
||||
//msg = "<space=0.376em>"+msg+".";
|
||||
//LoadingScreen.SetLoadingInfo(msg);
|
||||
//innertimer=0;
|
||||
//}
|
||||
//if(innerTimer2>1.2f){
|
||||
//msg=msg_mem;
|
||||
//LoadingScreen.SetLoadingInfo(msg);
|
||||
//innerTimer2=0;
|
||||
//innertimer=0;
|
||||
//}
|
||||
//if(timer>2.4f){
|
||||
//msg = LocalizationMgr.GetRandomText();
|
||||
//msg_mem = msg;
|
||||
//LoadingScreen.SetLoadingInfo(msg);
|
||||
//timer=0;
|
||||
//innertimer = 0;
|
||||
//innerTimer2 = 0;
|
||||
//}
|
||||
//progress = Mathf.Min(progress + 0.01f, total);
|
||||
//LoadingScreen.SetProgress(progress);
|
||||
//await Task.Delay(100);
|
||||
//}
|
||||
//}
|
||||
//catch (System.Exception)
|
||||
//{
|
||||
//LoadingScreen.SetProgress(0);
|
||||
//LoadingScreen.HideProgressBar();
|
||||
//LoadingScreen.SetLoadingInfo(string.Empty);
|
||||
//}
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using Game;
|
||||
using GameCore;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class QuitAct : AGameAct
|
||||
{
|
||||
[Inject]
|
||||
public IConfig config { get; set; }
|
||||
|
||||
[Inject]
|
||||
public IRTService rtService { get; set; }
|
||||
|
||||
[Inject]
|
||||
public IUserService userService { get; set; }
|
||||
|
||||
[Inject]
|
||||
public SoundMgr soundMgr { get; set; }
|
||||
|
||||
[Inject]
|
||||
public IAdsService adsService { get; set; }
|
||||
|
||||
#if AGG
|
||||
[Inject]
|
||||
public HeartBeat heartBeat {get; set;}
|
||||
#endif
|
||||
|
||||
protected override void OnDestroy() { }
|
||||
|
||||
public string[] DestoryRoot;
|
||||
|
||||
public override async Task<bool> StartAsync()
|
||||
{
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) _=game.PlayerDataStorage.PlayerKVGameplayLoop.CloseAsync();
|
||||
#endif
|
||||
if(userService is PlayerDataStorage.IKVUserLifecycle closingKVUser) _=closingKVUser.CloseKVForValidationAsync();
|
||||
// Invalidate identity before any await can allow old login/HTTP callbacks to resume.
|
||||
if (QuitStage.Logout)
|
||||
{
|
||||
QuitStage.Logout = false;
|
||||
try { userService.Logout(); }
|
||||
catch (System.Exception ex) { Debug.LogException(ex); }
|
||||
}
|
||||
LoadingScreen.Hide();
|
||||
GameObject.Destroy(UIManager.Instance.gameObject);
|
||||
|
||||
config.Rest();
|
||||
|
||||
try { await rtService.ReleaseAsync(); }
|
||||
catch (System.Exception ex) { Debug.LogException(ex); }
|
||||
|
||||
// Observation is bounded; the process registry still owns any unfinished close.
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if(game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled) await PlayerDataStorage.PlayerKVShutdownRegistry.Process.ObserveAsync(5000);
|
||||
else
|
||||
#endif
|
||||
if(userService is PlayerDataStorage.IKVUserLifecycle kvUser) await kvUser.ObserveKVQuitAsync();
|
||||
|
||||
adsService.Release();
|
||||
|
||||
GameEventMgr.Instance.Clear();
|
||||
|
||||
#if AGG
|
||||
heartBeat?.Dispose();
|
||||
#endif
|
||||
GameCore.Timer.CancelAllRegisteredTimers();
|
||||
GameCore.Timer.Destroy();
|
||||
|
||||
soundMgr.Release();
|
||||
RedPointManager.Instance.Init();
|
||||
return await base.StartAsync();
|
||||
}
|
||||
|
||||
protected override void OnStarted()
|
||||
{
|
||||
RootCtx.DestoryRootCtx();
|
||||
for (int i = 0; i < DestoryRoot.Length; i++)
|
||||
{
|
||||
GameObject go = GameObject.Find(DestoryRoot[i]);
|
||||
if (go != null)
|
||||
{
|
||||
GameObject.DestroyImmediate(go);
|
||||
}
|
||||
}
|
||||
base.OnStarted();
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
if (game.PlayerDataStorage.PlayerKVGameplayLoop.Enabled)
|
||||
{
|
||||
var status = PlayerDataStorage.PlayerKVShutdownRegistry.Process.Status;
|
||||
Debug.Log("KV loop ended; restart Unity before another run. Shutdown: " + status);
|
||||
if (status == PlayerDataStorage.PlayerKVShutdownStatus.Ready)
|
||||
UnityEditor.EditorApplication.isPlaying = false;
|
||||
return; // Keep the process barrier; never return this run to PreBoot.
|
||||
}
|
||||
#endif
|
||||
Invoke("Unload", 2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
|
||||
using AppsFlyerSDK;
|
||||
using asap.core;
|
||||
using asap.core.common;
|
||||
using DataCenter;
|
||||
using game;
|
||||
using Game;
|
||||
using GameCore;
|
||||
using MoreMountains.NiceVibrations;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Script.RuntimeScript;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ThinkingData.Analytics;
|
||||
using TimeManager;
|
||||
using tysdk;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
public class SubBoostrap
|
||||
{
|
||||
internal static game.PlayerDataStorage.PlayerKVRuntimeSelection RuntimeSelection { get; private set; }
|
||||
|
||||
public static async System.Threading.Tasks.Task RegisterServices(GameObject rootCtxGo, IConfig config)
|
||||
{
|
||||
if(config==null) throw new ArgumentNullException(nameof(config));
|
||||
#if AGG
|
||||
const bool aggBuild=true;
|
||||
#else
|
||||
const bool aggBuild=false;
|
||||
#endif
|
||||
var selection=game.PlayerDataStorage.PlayerKVRuntimeSelection.Capture(
|
||||
config.Get<string>("SaveBackend","playfab"),
|
||||
config.Get<string>(GConstant.K_Event_API_URL,game.Account.S101AccountSession.DefaultApiUrl),aggBuild);
|
||||
RuntimeSelection=selection;
|
||||
var container = GContext.container;
|
||||
container.RegisterInstance(selection);
|
||||
VibrationController vibrationController = new VibrationController();
|
||||
vibrationController.Init();
|
||||
container.RegisterInstance(vibrationController);
|
||||
container.Register<IObjectPoolService, ObjectPoolService>().AsSingleton();
|
||||
|
||||
|
||||
#if AGG && !UNITY_EDITOR //AIHelp init
|
||||
//string appKey = "TUYOOGAME_app_a6e73dd23da84d5ab72a5f68e4a58271";
|
||||
string domain = "tuyoogame.aihelp.net";
|
||||
#if UNITY_ANDROID
|
||||
string appId = "tuyoogame_platform_7a6a236be774b7a82c23d3e59f7d1476";
|
||||
#elif UNITY_IOS
|
||||
string appId = "tuyoogame_platform_782757bd1f7ba24498cd9df0d0b9f214";
|
||||
#endif
|
||||
|
||||
AIHelp.AIHelpSupport.enableLogging(false);
|
||||
AIHelp.AIHelpSupport.AdditionalSupportFor(AIHelp.PublishCountryOrRegion.IN);
|
||||
|
||||
AIHelp.AIHelpSupport.RegisterAsyncEventListener(AIHelp.EventType.Initialization, (jsonEventData, ack)
|
||||
=>Debug.Log($"[HotUpateBoostrap] AIHelpInitialized {JObject.Parse(jsonEventData).ToString()}"));
|
||||
|
||||
AIHelp.AIHelpSupport.Initialize(domain, appId);
|
||||
|
||||
#endif //AIHelp init end
|
||||
|
||||
#if AGG
|
||||
var heartBeat = new HeartBeat();
|
||||
#endif
|
||||
using (container.InjectionScope())
|
||||
{
|
||||
#if AGG
|
||||
container.RegisterInstance(heartBeat);
|
||||
#endif
|
||||
container.Register<ISoundService, SoundService>().AsSingleton();
|
||||
container.Register<ILoadResourceService, LoadResourceService>().AsSingleton();
|
||||
container.Register<SoundMgr>().AsSingleton();
|
||||
container.Register<TileNewsManager>().AsSingleton();
|
||||
}
|
||||
|
||||
#if AGG
|
||||
heartBeat.Init();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* _____ ___ ____ ___
|
||||
*|_ _/ _ \| _ \ / _ \
|
||||
* | || | | | | | | | | |
|
||||
* | || |_| | |_| | |_| |
|
||||
* |_| \___/|____/ \___/
|
||||
*/
|
||||
// TODO Refactor all managers
|
||||
#if UNITY_EDITOR
|
||||
UnityEngine.Application.runInBackground = true;
|
||||
GameCore.GameEventMgr.Init(true);
|
||||
#else
|
||||
GameCore.GameEventMgr.Init();
|
||||
#endif
|
||||
#if UNITY_EDITOR && !SERVICE_HOST
|
||||
game.PlayerDataStorage.PlayerKVGameplayLoop.BeforeBootstrap();
|
||||
#endif
|
||||
if(!selection.IsKV) rootCtxGo.AddComponent<PlayFabMgr>();
|
||||
// TODO Replace DataCenter with seprated data class
|
||||
// Create();
|
||||
// -----------------------------------------
|
||||
await Awaiters.NextFrame;
|
||||
GameCore.Timer.Create();
|
||||
//-----------------------TODO--------------------------
|
||||
Debug.Log("[HotUpateBoostrap] RegisterServices Done");
|
||||
await Awaiters.NextFrame;
|
||||
}
|
||||
|
||||
public static async System.Threading.Tasks.Task AfterAssetUpdate(IConfig config, Action<float> progressAction)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
var tables = await cfg.Tables.InitEditor(progress => progressAction(0.16f + progress * 0.02f));
|
||||
#else
|
||||
var tables = await cfg.Tables.Init(progress => progressAction(0.16f + progress * 0.02f));
|
||||
#endif
|
||||
var container = GContext.container;
|
||||
GContext.container.RegisterInstance(tables);
|
||||
GContext.container.Register<IFaceUIService, FaceUIService>().AsSingleton();
|
||||
GContext.container.Register<IUIService, UIService>().AsSingleton();
|
||||
GContext.container.Register<IChatService, ChatService>().AsSingleton();
|
||||
GContext.container.Register<IReportService, ReportService>().AsSingleton();
|
||||
GContext.container.Register<ILocalNotificationService, LocalNotificationService>().AsSingleton();
|
||||
GContext.container.Register<ICustomServerMgr, CustomServerMgr>().AsSingleton();
|
||||
GContext.container.Register<IInGameMailService, InGameMailService>().AsSingleton();
|
||||
GContext.container.Register<IRTService, SignalRService>().AsSingleton();
|
||||
GContext.container.RegisterInstance(new AvatarService());
|
||||
|
||||
var settings = new DefaultSettingService(RuntimeSelection?.IsKV == true);
|
||||
settings.Init(tables);
|
||||
GContext.container.RegisterInstance<ISettingService>(settings);
|
||||
GameCore.LocalizationMgr.Init();
|
||||
QualityManager.AutoSetQualityLevel();
|
||||
RootCtx.showFPS = !string.IsNullOrEmpty(config.Get(GConstant.K_FPS, string.Empty));
|
||||
DebugFunc.Init();
|
||||
|
||||
zzwater.URPHelper.RendererFeatureSetActive(false, "FSBlur");
|
||||
zzwater.URPHelper.RendererFeatureSetActive(false, "FSWave");
|
||||
|
||||
await InitVibration();
|
||||
//#if UNITY_ANDROID
|
||||
//RequestAndriodPermission();
|
||||
//#endif
|
||||
await Awaiters.NextFrame;
|
||||
|
||||
await GContext.container.Resolve<ILocalNotificationService>().Init();
|
||||
|
||||
IAdsService adsService;
|
||||
|
||||
if (Application.isEditor)
|
||||
{
|
||||
adsService = new DevAdsService();
|
||||
}
|
||||
else
|
||||
{
|
||||
adsService = new AggAdsService(settings);
|
||||
}
|
||||
adsService.Init();
|
||||
GContext.container.RegisterInstance<IAdsService>(adsService);
|
||||
|
||||
Debug.Log("[HotUpateBoostrap] AfterAssetUpdate Done");
|
||||
|
||||
}
|
||||
|
||||
public static async System.Threading.Tasks.Task ShowLoadingScreen(IConfig config, GameObject rootCtxGo)
|
||||
{
|
||||
var fxClick = await Addressables.InstantiateAsync("TopUIFX").Task;
|
||||
if (fxClick != null)
|
||||
{
|
||||
fxClick.name = "TopUIFX";
|
||||
GameObject.DontDestroyOnLoad(fxClick);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("TopUIFX is null");
|
||||
}
|
||||
|
||||
// TODO replace with new UI service
|
||||
await GameCore.UIManager.Init(
|
||||
UITypes.UIRoot, GameCore.ScreenType.PORTRAIT,
|
||||
() => MessageDialog.ShowErrorDialog(-20, "Default Waiting Timeout")
|
||||
);
|
||||
|
||||
var uiCam = rootCtxGo.FindChildGameObject("UICamera").GetComponent<Camera>();
|
||||
var ui3DCam = rootCtxGo.FindChildGameObject("UI3DCamera").GetComponent<Camera>();
|
||||
UIManager.SetUICamera(uiCam, ui3DCam);
|
||||
|
||||
//Camera.main.SetCamStack();
|
||||
await LoadingScreen.Show(0.19f);
|
||||
|
||||
|
||||
//LoadingScreen.SetVersionInfo($"Ver {Application.version}");
|
||||
var reVer = VersionTool.FromString(config.Get(GConstant.K_Ver, string.Empty));
|
||||
reVer.Save();
|
||||
LoadingScreen.SetVersionInfo(reVer.ToVerString());
|
||||
|
||||
Debug.Log("[HotUpateBoostrap] ShowLoadingScreen Done");
|
||||
}
|
||||
|
||||
private static async Task InitVibration()
|
||||
{
|
||||
var data = await Addressables.LoadAssetAsync<TextAsset>("vibrationdata").Task;
|
||||
if (null != data)
|
||||
{
|
||||
var jsonData = JObject.Parse(data.text);
|
||||
if (jsonData.ContainsKey("Soft"))
|
||||
{
|
||||
var soft = jsonData["Soft"];
|
||||
MMVibrationManager.SoftDuration = soft["duration"].Value<long>();
|
||||
MMVibrationManager.SoftAmplitude = soft["amplitude"].Value<int>();
|
||||
}
|
||||
if (jsonData.ContainsKey("Rigid"))
|
||||
{
|
||||
var rigid = jsonData["Rigid"];
|
||||
MMVibrationManager.RigidDuration = rigid["duration"].Value<long>();
|
||||
MMVibrationManager.RigidAmplitude = rigid["amplitude"].Value<int>();
|
||||
}
|
||||
if (jsonData.ContainsKey("Light"))
|
||||
{
|
||||
var light = jsonData["Light"];
|
||||
MMVibrationManager.LightDuration = light["duration"].Value<long>();
|
||||
MMVibrationManager.LightAmplitude = light["amplitude"].Value<int>();
|
||||
}
|
||||
if (jsonData.ContainsKey("Heavy"))
|
||||
{
|
||||
var heavy = jsonData["Heavy"];
|
||||
MMVibrationManager.LightDuration = heavy["duration"].Value<long>();
|
||||
MMVibrationManager.LightAmplitude = heavy["amplitude"].Value<int>();
|
||||
}
|
||||
if (jsonData.ContainsKey("Medium"))
|
||||
{
|
||||
var medium = jsonData["Medium"];
|
||||
MMVibrationManager.LightDuration = medium["duration"].Value<long>();
|
||||
MMVibrationManager.LightAmplitude = medium["amplitude"].Value<int>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#if UNITY_ANDROID
|
||||
//private static void RequestAndriodPermission()
|
||||
//{
|
||||
//if(!UnityEngine.Android.Permission.HasUserAuthorizedPermission("android.permission.QUERY_ALL_PACKAGES"))
|
||||
//{
|
||||
//UnityEngine.Android.Permission.RequestUserPermission("android.permission.QUERY_ALL_PACKAGES");
|
||||
//}
|
||||
//}
|
||||
//#endif
|
||||
}
|
||||
|
||||
// public static class CameraExtension
|
||||
// {
|
||||
// public static void SetCamStack(this Camera cam)
|
||||
// {
|
||||
// if (cam != Camera.main) return;
|
||||
// // For URP rendering, add UICamera into main scene camera's stack
|
||||
// //
|
||||
// Camera ui_cam = null;
|
||||
//
|
||||
// if (GameCore.UIManager.Instance != null)
|
||||
// {
|
||||
// ui_cam = GameCore.UIManager.Instance.UICamera;
|
||||
// }
|
||||
//
|
||||
// if (ui_cam != null && cam != ui_cam)
|
||||
// {
|
||||
// var cam_stack = cam.GetUniversalAdditionalCameraData().cameraStack;
|
||||
//
|
||||
// if (!cam_stack.Contains(ui_cam))
|
||||
// {
|
||||
// cam_stack.Add(ui_cam);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public class InternalEditorBoost
|
||||
{
|
||||
public static async Task Boost()
|
||||
{
|
||||
UnityEngine.Debug.Log("<color=orange>Editor Internal Boot</color>");
|
||||
GContext.container.Register<IUIService, UIService>().AsSingleton();
|
||||
|
||||
await GameCore.UIManager.Init(
|
||||
UITypes.UIRoot, GameCore.ScreenType.PORTRAIT,
|
||||
() => MessageDialog.ShowErrorDialog(-20, "Default Waiting Timeout")
|
||||
);
|
||||
var ieb = new InternalEditorBoost();
|
||||
await ieb.LoadConfig();
|
||||
await ieb.Init();
|
||||
}
|
||||
|
||||
private async Task LoadConfig()
|
||||
{
|
||||
var config = GContext.container.Resolve<IConfig>();
|
||||
// Load Debug Config
|
||||
var debugConfigPath = Path.Combine(Application.persistentDataPath, GConstant.V_Debug_Config_Path);
|
||||
string remoteConfigUrl = null;
|
||||
SimpleJSON.JSONNode debugConfig = null;
|
||||
|
||||
if (File.Exists(debugConfigPath))
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"<color=yellow>Debug config [{debugConfigPath}] in use</color>");
|
||||
#endif
|
||||
debugConfig = SimpleJSON.JSON.Parse(File.ReadAllText(debugConfigPath));
|
||||
if (debugConfig.HasKey(GConstant.K_Static_Res_URL))
|
||||
{
|
||||
remoteConfigUrl = debugConfig[GConstant.K_Static_Res_URL];
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"<color=yellow>Debug Static Res URL => {remoteConfigUrl}</color>");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
var staticResURL = GConstant.V_Static_Res_URL;
|
||||
if (ChannelManager.IsRustoreStore)
|
||||
staticResURL = GConstant.V_Static_Res_URL_RU;
|
||||
|
||||
// load remote Config
|
||||
remoteConfigUrl = remoteConfigUrl ?? string.Format(staticResURL, VersionTool.PackVer);
|
||||
|
||||
var request = UnityWebRequest.Get($"{remoteConfigUrl}/{GConstant.V_Remote_Config_Path}");
|
||||
await request.SendWebRequest();
|
||||
if (request.result != UnityWebRequest.Result.Success)
|
||||
throw new Exception($"Load Config Error: {request.error}");
|
||||
|
||||
string remoteConfigStr = request.downloadHandler.text;
|
||||
remoteConfigStr = Decrypt(remoteConfigStr);
|
||||
var remoteConfig = SimpleJSON.JSON.Parse(remoteConfigStr);
|
||||
// Merge Configs
|
||||
foreach (var element in remoteConfig)
|
||||
{
|
||||
config.Set(element.Key, element.Value.Value);
|
||||
}
|
||||
|
||||
if (null != debugConfig)
|
||||
{
|
||||
foreach (var element in debugConfig)
|
||||
{
|
||||
config.Set(element.Key, element.Value.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// Change Addressable remote location, using custom assetbundle server
|
||||
var customAddressableRemoteURL = config.Get(GConstant.K_Custom_Addressable, string.Empty);
|
||||
var originAddressableRemoteURL = config.Get(GConstant.K_Origin_Addressable, string.Empty);
|
||||
if (!string.IsNullOrEmpty(customAddressableRemoteURL)
|
||||
&& !string.IsNullOrEmpty(originAddressableRemoteURL))
|
||||
{
|
||||
var ctx = (RootCtx)typeof(GContext).GetField("root", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null);
|
||||
|
||||
ctx.ChangeAddressableRemoteUrl(customAddressableRemoteURL, originAddressableRemoteURL);
|
||||
}
|
||||
|
||||
}
|
||||
string RTMKey = "tbambooz";
|
||||
public string Decrypt(string text)
|
||||
{
|
||||
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
|
||||
des.Key = Encoding.ASCII.GetBytes(RTMKey);
|
||||
des.IV = Encoding.ASCII.GetBytes(RTMKey);
|
||||
|
||||
|
||||
ICryptoTransform decryptor = des.CreateDecryptor(des.Key, des.IV);
|
||||
byte[] inputBytes = Convert.FromBase64String(text);
|
||||
byte[] outputBytes = decryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length);
|
||||
|
||||
return Encoding.UTF8.GetString(outputBytes);
|
||||
}
|
||||
private async Task Init()
|
||||
{
|
||||
var selection = GContext.container.Resolve<game.PlayerDataStorage.PlayerKVRuntimeSelection>();
|
||||
if (selection == null)
|
||||
{
|
||||
var startupConfig = GContext.container.Resolve<IConfig>();
|
||||
var backend = startupConfig?.Get<string>("SaveBackend", "playfab");
|
||||
if (!string.Equals(backend, "playfab", StringComparison.Ordinal))
|
||||
throw new InvalidOperationException("Editor direct scene boot only supports playfab; KV requires RegisterServices.");
|
||||
// Editor direct scene boot is PF-only; KV and kv-loop must use RegisterServices.
|
||||
selection = game.PlayerDataStorage.PlayerKVRuntimeSelection.Capture("playfab", null, false);
|
||||
GContext.container.RegisterInstance(selection);
|
||||
if (!ReferenceEquals(GContext.container.Resolve<game.PlayerDataStorage.PlayerKVRuntimeSelection>(), selection))
|
||||
throw new InvalidOperationException("Editor backend selection registration failed.");
|
||||
}
|
||||
if (selection.IsKV)
|
||||
throw new InvalidOperationException("Editor direct scene boot cannot reuse a KV selection.");
|
||||
var tables = await cfg.Tables.Init(x => { Debug.Log(x); });
|
||||
GContext.container.RegisterInstance(tables);
|
||||
GContext.container.Register<IFaceUIService, FaceUIService>().AsSingleton();
|
||||
GContext.container.Register<IUIService, UIService>().AsSingleton();
|
||||
GContext.container.Register<ICustomServerMgr, CustomServerMgr>().AsSingleton();
|
||||
GContext.container.Register<IChatService, ChatService>().AsSingleton();
|
||||
GContext.container.Register<ILocalNotificationService, LocalNotificationService>().AsSingleton();
|
||||
GContext.container.Register<IReportService, ReportService>().AsSingleton();
|
||||
GContext.container.Register<ModuleManager.ModuleManager>().AsSingleton();
|
||||
GContext.container.Register<IInGameMailService, InGameMailService>().AsSingleton();
|
||||
|
||||
using (var scope = GContext.container.InjectionScope())
|
||||
{
|
||||
GContext.container.Register<game.PlayerDataStorage.IPlayerSaveStore, game.PlayerDataStorage.PlayFabPlayerSaveStore>().AsSingleton();
|
||||
// IAdsService 在 AfterAssetUpdate 中手动注册并调用 Init(),此处不再重复注册
|
||||
GContext.container.Register<PlayerFishData>().AsSingleton();
|
||||
GContext.container.Register<PlayerItemData>().AsSingleton();
|
||||
GContext.container.Register<CampDataMM>().AsSingleton();
|
||||
GContext.container.Register<FishingEventData>().AsSingleton();
|
||||
GContext.container.Register<SigninData>().AsSingleton();
|
||||
//GContext.container.Register<TaskData>().AsSingleton();
|
||||
GContext.container.Register<ClubService>().AsSingleton();
|
||||
GContext.container.Register<LeadboardData>().AsSingleton();
|
||||
GContext.container.Register<PlayerShopData>().AsSingleton();
|
||||
GContext.container.Register<TriggerPackData>().AsSingleton();
|
||||
GContext.container.Register<GuideDataCenter>().AsSingleton();
|
||||
GContext.container.Register<SmallGameData>().AsSingleton();
|
||||
GContext.container.Register<PlayerData>().AsSingleton();
|
||||
GContext.container.Register<SoundMgr>().AsSingleton();
|
||||
GContext.container.Register<AvatarService>().AsSingleton();
|
||||
GContext.container.Register<BuffDataCenter>().AsSingleton();
|
||||
GContext.container.Register<FishingBoxData>().AsSingleton();
|
||||
GContext.container.Register<FishingBoxDataProvier>().AsSingleton();
|
||||
// GContext.container.Register<FishingChallengeCenter>().AsSingleton();
|
||||
GContext.container.Register<FishingChallengeManager>().AsSingleton();
|
||||
GContext.container.Register<OfferChainsChestManager>().AsSingleton();
|
||||
GContext.container.Register<OfferDiscoPartyManager>().AsSingleton();
|
||||
GContext.container.Register<BattlePassDataProvider>().AsSingleton();
|
||||
GContext.container.Register<MiniBattlePassDataProvider>().AsSingleton();
|
||||
// GContext.container.Register<OfferRushDataProvider>().AsSingleton();
|
||||
GContext.container.Register<BattlePassData>().AsSingleton();
|
||||
GContext.container.Register<EventPackData>().AsSingleton();
|
||||
GContext.container.Register<AchievementDataManager>().AsSingleton();
|
||||
//GContext.container.Register<PokerScoreData>().AsSingleton();
|
||||
//GContext.container.Register<RedeemCodeService>().AsSingleton();
|
||||
GContext.container.Register<PiggyBankPackData>().AsSingleton();
|
||||
GContext.container.Register<BargainPackData>().AsSingleton();
|
||||
GContext.container.Register<DiggingGameManager>().AsSingleton();
|
||||
GContext.container.Register<EventPartnerData>().AsSingleton();
|
||||
GContext.container.Register<EventPinataPackDataManager>().AsSingleton();
|
||||
GContext.container.Register<Assets.Scripts.UI.OfferSweep.OfferSweepManager>().AsSingleton();
|
||||
GContext.container.Register<Assets.Scripts.UI.AdWheel.AdWheelManager>().AsSingleton();
|
||||
GContext.container.Register<Assets.Scripts.UI.JigsawPuzzle.JigsawPuzzleManager>().AsSingleton();
|
||||
|
||||
GContext.container.Register<AntiCheatManager>().AsSingleton();
|
||||
GContext.container.Register<Activity.ActivityManager>().AsSingleton();
|
||||
GContext.container.Register<Activity.ActivityScopeManager>().AsSingleton();
|
||||
GContext.container.Register<TimeRefreshManager>().AsSingleton();
|
||||
}
|
||||
var settings = new DefaultSettingService(selection.IsKV);
|
||||
settings.Init(tables);
|
||||
GContext.container.RegisterInstance<ISettingService>(settings);
|
||||
GameCore.LocalizationMgr.Init();
|
||||
await GContext.container.Resolve<ILocalNotificationService>().Init();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using asap.core;
|
||||
using game;
|
||||
using GameCore;
|
||||
using TimeManager.Event;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TimeManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 时间刷新管理器 - 检测跨天、跨小时并触发刷新。须在启动链中显式 <c>Resolve<TimeRefreshManager>()</c>(见 ActivityManager.Init)。
|
||||
/// </summary>
|
||||
public class TimeRefreshManager : IDisposable
|
||||
{
|
||||
public TimeRefreshManager(ITimeTickService tickService, IUserService userService)
|
||||
{
|
||||
_tickService = tickService;
|
||||
_userService = userService;
|
||||
|
||||
_sessionStartTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
_lastCheckedHour = _sessionStartTime.Hour;
|
||||
InitialRefreshSucceeded = CheckLastLogin();
|
||||
_tickService.MinuteTick
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
CheckSessionCrossDay();
|
||||
CheckSessionCrossHour();
|
||||
})
|
||||
.AddTo(_disposables);
|
||||
}
|
||||
|
||||
private readonly ITimeTickService _tickService;
|
||||
private readonly IUserService _userService;
|
||||
public bool InitialRefreshSucceeded { get; }
|
||||
|
||||
private int _lastCheckedDayOfYear = -1;
|
||||
private int _lastCheckedHour = -1;
|
||||
private DateTime _sessionStartTime;
|
||||
private CompositeDisposable _disposables = new();
|
||||
|
||||
#region Check登录时间
|
||||
|
||||
private bool CheckLastLogin()
|
||||
{
|
||||
if (IsLastLoginToday()) return true;
|
||||
Debug.Log("上次登录不是今天");
|
||||
return OnCrossDayRefresh();
|
||||
}
|
||||
private void CheckSessionCrossDay()
|
||||
{
|
||||
if (!IsSessionCrossDay()) return;
|
||||
OnCrossDayRefresh();
|
||||
_sessionStartTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
}
|
||||
|
||||
private void CheckSessionCrossHour()
|
||||
{
|
||||
if (!IsSessionCrossHour()) return;
|
||||
OnCrossHourRefresh();
|
||||
var now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
_lastCheckedHour = now.Hour;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region 登录时间相关
|
||||
|
||||
/// <summary>
|
||||
/// 判断上次登录是否是今天
|
||||
/// 从 UserService 获取 PlayFab 记录的真实上次登录时间
|
||||
/// </summary>
|
||||
public bool IsLastLoginToday()
|
||||
{
|
||||
var lastLoginTime = GetLastLoginTime();
|
||||
if (!lastLoginTime.HasValue)
|
||||
{
|
||||
return false; // 从未登录过
|
||||
}
|
||||
|
||||
var now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
return lastLoginTime.Value.DayOfYear == now.DayOfYear &&
|
||||
lastLoginTime.Value.Year == now.Year;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断当前会话是否跨天(从登录到现在的过程中是否跨过了午夜)
|
||||
/// </summary>
|
||||
public bool IsSessionCrossDay()
|
||||
{
|
||||
var now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
return _sessionStartTime.DayOfYear != now.DayOfYear ||
|
||||
_sessionStartTime.Year != now.Year;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断当前会话是否跨小时(从上次检查到现在是否跨过了整点)
|
||||
/// </summary>
|
||||
public bool IsSessionCrossHour()
|
||||
{
|
||||
var now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
return now.Hour != _lastCheckedHour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取上次登录时间(从 UserService 获取)
|
||||
/// </summary>
|
||||
public DateTime? GetLastLoginTime()
|
||||
{
|
||||
var value = _userService?.LastLoginTime;
|
||||
return value == DateTime.MinValue ? null : value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取会话开始时间(本次游戏启动时间)
|
||||
/// </summary>
|
||||
public DateTime GetSessionStartTime()
|
||||
{
|
||||
return _sessionStartTime;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 跨天刷新逻辑
|
||||
/// </summary>
|
||||
private bool OnCrossDayRefresh()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 发送活动跨天刷新事件
|
||||
GContext.Publish(new ActivityCrossDayRefreshEvent());
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[TimeRefreshManager] Error during cross day refresh: {e.Message}\n{e.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 跨小时刷新逻辑
|
||||
/// </summary>
|
||||
private void OnCrossHourRefresh()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 发送跨小时刷新事件
|
||||
GContext.Publish(new ActivityCrossHourRefreshEvent());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[TimeRefreshManager] Error during cross hour refresh: {e.Message}\n{e.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposables?.Dispose();
|
||||
_disposables = null;
|
||||
Debug.Log("[TimeRefreshManager] Disposed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UI.Wheel;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.UI.AdWheel
|
||||
{
|
||||
public class AdWheelManager:IDisposable
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
|
||||
public AdWheelManager()
|
||||
{
|
||||
GContext.OnEvent<TargetEvent>().Subscribe(OnEvent_TargetEvent).AddTo(Disposables);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposableSimple?.Dispose();
|
||||
Disposables?.Dispose();
|
||||
}
|
||||
|
||||
private CompositeDisposable _disposable;
|
||||
private CompositeDisposable Disposables => _disposable ??= new CompositeDisposable();
|
||||
#region event
|
||||
private void OnEvent_TargetEvent(TargetEvent targetEvent)
|
||||
{
|
||||
if (targetEvent.type == 2 && targetEvent.subType == 4)
|
||||
{
|
||||
//只有一个广告转盘
|
||||
RefreshData(targetEvent.id);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region data
|
||||
[Inject]
|
||||
public Tables Tables { get; set; }
|
||||
private AdWheelData _adWheelData;
|
||||
public AdWheelData Data => _adWheelData ??= new AdWheelData(Tables);
|
||||
private void RefreshData(int eventId)
|
||||
{
|
||||
//判断开启时间
|
||||
var fishingEvent = Tables.TbFishingEvent.GetOrDefault(eventId);
|
||||
if (fishingEvent == null) return;
|
||||
var daily = fishingEvent.TimeDefinition as Daily;
|
||||
var now = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
var refreshTime = new DateTimeOffset(now).ToUnixTimeSeconds() + (daily.EndTime - (long)now.TimeOfDay.TotalSeconds);
|
||||
var cacheData = GetCachedData();
|
||||
if (cacheData != null && cacheData.ActiveEventTime > 0)
|
||||
{
|
||||
this.Data.SetData(cacheData);
|
||||
if (this.Data.EventRefreshTimeSeconds != refreshTime)
|
||||
{
|
||||
this.Data.EventRefreshTimeSeconds = refreshTime;
|
||||
this.Data.ClearProgress();
|
||||
SaveData();
|
||||
}
|
||||
if (this.Data.EventID != eventId)
|
||||
{
|
||||
this.Data.EventID = eventId;
|
||||
SaveData();
|
||||
}
|
||||
SetRedPoint();
|
||||
CheckIsAddGiftFaceUI();
|
||||
return;
|
||||
}
|
||||
|
||||
bool isOpenEvent = IsOpenEvent(eventId);
|
||||
if (!isOpenEvent) return;
|
||||
|
||||
InitData(eventId, now, refreshTime);
|
||||
SaveData();
|
||||
}
|
||||
|
||||
private void InitData(int eventId, DateTime now, long refreshTime)
|
||||
{
|
||||
var nowTimeSeconds = new DateTimeOffset(now).ToUnixTimeSeconds();
|
||||
this.Data.EventID = eventId;
|
||||
this.Data.lastViewAdTime = 0;
|
||||
this.Data.ActiveEventTime = nowTimeSeconds;
|
||||
this.Data.EventRefreshTimeSeconds = refreshTime;
|
||||
this.Data.ClearProgress();
|
||||
CheckIsAddGiftFaceUI();
|
||||
}
|
||||
|
||||
private void CheckIsAddGiftFaceUI()
|
||||
{
|
||||
if(Data.IsActive&&GetRedPointState())
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(Data.EventID, UITypes.EventBonusWheelPopupPanel, 0);
|
||||
}
|
||||
|
||||
private AdWheelData GetCachedData()
|
||||
{
|
||||
var dataStr = saveStore.Read(nameof(AdWheelData)).ValueOrThrow();
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd AdWheelData LoadData sData {dataStr}");
|
||||
#endif
|
||||
return dataStr == null ? null : JsonConvert.DeserializeObject<AdWheelData>(dataStr) ?? throw new JsonSerializationException("Null AdWheelData save.");
|
||||
}
|
||||
|
||||
private bool IsOpenEvent(int eventId)
|
||||
{
|
||||
var eventConfig = Tables.TbFishingEvent.GetOrDefault(eventId);
|
||||
if (eventConfig == null) return true;
|
||||
var isOpen = GContext.container.Resolve<FishingEventData>()?.Condition(eventConfig) ?? false;
|
||||
return isOpen;
|
||||
}
|
||||
public void SaveData()
|
||||
{
|
||||
SetRedPoint();
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd AdWheelData SaveData {JsonConvert.SerializeObject(Data)}");
|
||||
#endif
|
||||
saveStore.Write(nameof(AdWheelData), JsonConvert.SerializeObject(Data)).RequireAccepted();
|
||||
}
|
||||
|
||||
private List<IWheelReward> _rewards = new List<IWheelReward>();
|
||||
public List<IWheelReward> GetEventAdWheelRewards()
|
||||
{
|
||||
if (Data == null) return null;
|
||||
|
||||
if (_rewards == null)
|
||||
_rewards = new List<IWheelReward>();
|
||||
else
|
||||
_rewards.Clear();
|
||||
var adWheelMainID = Data.EventConfig.RedirectID;
|
||||
if (adWheelMainID <= 0) return null;
|
||||
|
||||
var adWheelMain = Data.AdWheelMain;
|
||||
if (adWheelMain == null) return null;
|
||||
|
||||
foreach (var id in adWheelMain.WheelRewardList)
|
||||
{
|
||||
var rewardConfig = Tables.TbEventAdWheelReward.Get(id);
|
||||
if (rewardConfig == null
|
||||
|| _rewards.Find(x => x.Index == rewardConfig.Index) != null)
|
||||
continue;
|
||||
_rewards.Add(new EventAdWheelRewardAdapter(rewardConfig,0));
|
||||
|
||||
}
|
||||
CalculateWeights();
|
||||
return _rewards;
|
||||
}
|
||||
|
||||
// 缓存数据
|
||||
private int totalWeight = 0;
|
||||
private List<int> weightRanges = new List<int>();
|
||||
|
||||
private void CalculateWeights()
|
||||
{
|
||||
totalWeight = _rewards.Sum(r => r.Weight);
|
||||
BuildWeightRanges();
|
||||
}
|
||||
private void BuildWeightRanges()
|
||||
{
|
||||
weightRanges.Clear();
|
||||
int currentWeight = 0;
|
||||
|
||||
foreach (var reward in _rewards)
|
||||
{
|
||||
currentWeight += reward.Weight;
|
||||
weightRanges.Add(currentWeight);
|
||||
}
|
||||
}
|
||||
|
||||
// 根据权重随机获取奖励
|
||||
public int GetRandomRewardIndex()
|
||||
{
|
||||
if (totalWeight <= 0)
|
||||
{
|
||||
Debug.LogError("总权重为0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int randomValue = UnityEngine.Random.Range(0, totalWeight);
|
||||
|
||||
for (int i = 0; i < weightRanges.Count; i++)
|
||||
{
|
||||
if (randomValue < weightRanges[i])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return _rewards.Count - 1;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static List<ItemData> GetProgressRewards(AdWheelData adWheelData)
|
||||
{
|
||||
if (adWheelData == null) return null;
|
||||
|
||||
var adWheelMainID = adWheelData.EventConfig.RedirectID;
|
||||
if (adWheelMainID <= 0) return null;
|
||||
|
||||
var adWheelMain = adWheelData.AdWheelMain;
|
||||
if (adWheelMain == null) return null;
|
||||
|
||||
List<ItemData> rewards = new List<ItemData>();
|
||||
foreach (var dropID in adWheelMain.ProgressRewardDropIDList)
|
||||
{
|
||||
var itemData = GContext.container.Resolve<PlayerItemData>().GetItemDataOne(dropID);
|
||||
if (itemData != null)
|
||||
rewards.Add(itemData);
|
||||
}
|
||||
return rewards;
|
||||
}
|
||||
public static bool GetProgressRewardByProgress(AdWheelData adWheelData, out int dropID)
|
||||
{
|
||||
dropID = -1;
|
||||
if (adWheelData == null) return false;
|
||||
|
||||
var adWheelMainID = adWheelData.EventConfig.RedirectID;
|
||||
if (adWheelMainID <= 0) return false;
|
||||
|
||||
var adWheelMain = adWheelData.AdWheelMain;
|
||||
if (adWheelMain == null) return false;
|
||||
|
||||
if (adWheelMain.ProgressRewardADTimeList == null)
|
||||
{
|
||||
Debug.LogError($"AdWheelManager GetProgressRewardByProgress adWheelMain {adWheelMain.ID} ProgressRewardADTimeList is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
var index = adWheelMain.ProgressRewardADTimeList.IndexOf(adWheelData.Count);
|
||||
if (index == -1) return false;
|
||||
|
||||
if (adWheelMain.ProgressRewardDropIDList == null)
|
||||
{
|
||||
Debug.LogError($"AdWheelManager GetProgressRewardByProgress adWheelMain {adWheelMain.ID} ProgressRewardDropIDList is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index > adWheelMain.ProgressRewardDropIDList.Count - 1)
|
||||
{
|
||||
Debug.LogError($"AdWheelManager GetProgressRewardByProgress adWheelMain {adWheelMain.ID} index error index:{index}");
|
||||
return false;
|
||||
}
|
||||
dropID = adWheelMain.ProgressRewardDropIDList[index];
|
||||
return true;
|
||||
}
|
||||
public bool IsOpen(FishingEventData data, FishingEvent fishingEvent,
|
||||
Func<EventCondition,bool> getEventCondition,
|
||||
Func<int, bool> Condition_NoRechargeSeconds)
|
||||
{
|
||||
//开过一次一直开
|
||||
var cacheData = GetCachedData();
|
||||
if (cacheData is { ActiveEventTime: > 0 })
|
||||
return true;
|
||||
for (int i = 0; i < fishingEvent.ConditionList.Count; i++)
|
||||
{
|
||||
var condition= fishingEvent.ConditionList[i];
|
||||
if (condition.Condition == ConditionType.NoRechargeSeconds)
|
||||
{
|
||||
var mmt = GContext.container.Resolve<MMTService>();
|
||||
int time=int.Parse(condition.Param[0]);
|
||||
if (!Condition_NoRechargeSeconds(time)) return false;
|
||||
}
|
||||
else if(!getEventCondition(condition)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#region Red
|
||||
private IDisposable _disposableSimple;
|
||||
private string GetRedPointKey()
|
||||
{
|
||||
return "eventAdWheel.red";
|
||||
}
|
||||
private bool GetRedPointState()
|
||||
{
|
||||
if (Data?.AdWheelMain == null) return false;
|
||||
|
||||
var nowTime = new DateTimeOffset(ZZTimeHelper.UtcNow().UtcNowOffset()).ToUnixTimeSeconds();
|
||||
var cdTime = nowTime - Data.lastViewAdTime;
|
||||
var remainCd = Data.AdWheelMain.ADInterval - cdTime;
|
||||
var isCd = remainCd > 0;
|
||||
if (!isCd) return Data.Progerss < 1;
|
||||
_disposableSimple?.Dispose();
|
||||
_disposableSimple=Observable.Interval(TimeSpan.FromSeconds(remainCd)).Subscribe(_ =>
|
||||
{
|
||||
SetRedPoint();
|
||||
_disposableSimple?.Dispose();
|
||||
});
|
||||
return false;
|
||||
}
|
||||
private void SetRedPoint()
|
||||
{
|
||||
RedPointManager.Instance?.SetRedPointState(GetRedPointKey(), GetRedPointState());
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using asap.core;
|
||||
using Assets.Scripts.UI.AdWheel;
|
||||
using cfg;
|
||||
using DG.Tweening;
|
||||
using game;
|
||||
using GameCore;
|
||||
using TMPro;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
public class AdvertData
|
||||
{
|
||||
public int ID;
|
||||
public int index;
|
||||
public int day;
|
||||
public string cdTime;
|
||||
public bool isOpen;
|
||||
}
|
||||
public class AdvertPanel : MonoBehaviour
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
Button btn_close;
|
||||
TMP_Text txt_energy, txt_money, txt_ticket;
|
||||
ulong _curGold;
|
||||
int _curEnergy;
|
||||
int _curTicket;
|
||||
protected CompositeDisposable disposables = new CompositeDisposable();
|
||||
|
||||
|
||||
private Transform panel_gift1;
|
||||
private Transform panel_gift2;
|
||||
private Transform panel_gift3;
|
||||
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
txt_ticket = transform.Find("top_area/resource/icon_ticket/text_num").GetComponent<TMP_Text>();
|
||||
txt_energy = transform.Find("top_area/resource/icon_energy/text_num").GetComponent<TMP_Text>();
|
||||
txt_money = transform.Find("top_area/resource/icon_money/text_num").GetComponent<TMP_Text>();
|
||||
btn_close = transform.Find("root/btn_close").GetComponent<Button>();
|
||||
|
||||
//
|
||||
panel_gift1 = transform.Find("root/ScrollView/Viewport/Content/Item/panel_gift1");
|
||||
panel_gift2 = transform.Find("root/ScrollView/Viewport/Content/Item/panel_gift2");
|
||||
panel_gift3 = transform.Find("root/ScrollView/Viewport/Content/Item/panel_gift3");
|
||||
|
||||
}
|
||||
private void Start()
|
||||
{
|
||||
var playerData = GContext.container.Resolve<PlayerData>();
|
||||
_curGold = playerData.gold;
|
||||
_curEnergy = playerData.Energy;
|
||||
_curTicket = GContext.container.Resolve<PlayerShopData>().GetAdticketCount();
|
||||
txt_money.text = ConvertTools.GetNumberString2(_curGold);
|
||||
txt_energy.text = ConvertTools.GetNumberString2(_curEnergy);
|
||||
txt_ticket.text = ConvertTools.GetNumberString2(_curTicket);
|
||||
btn_close.onClick.AddListener(() =>
|
||||
{
|
||||
GContext.Publish(new RestartShowHomeUIEvent());
|
||||
UIManager.Instance.DestroyUI(UITypes.AdvertPanel);
|
||||
});
|
||||
GContext.OnEvent<ResAddEvent>().Where(x => x.id == 1002).Subscribe(x =>
|
||||
{
|
||||
GoldAddAnim();
|
||||
}).AddTo(disposables);
|
||||
GContext.OnEvent<ResAddEvent>().Where(x => x.id == 1001).Subscribe(x =>
|
||||
{
|
||||
EnergyAddAnim();
|
||||
}).AddTo(disposables);
|
||||
GContext.OnEvent<ResAddEvent>().Where(x => x.id == 1006).Subscribe(x =>
|
||||
{
|
||||
TicketAddAnim();
|
||||
}).AddTo(disposables);
|
||||
|
||||
GContext.OnEvent<ResAddEvent>().Where(x => x.id == 1007).Subscribe(x =>
|
||||
{
|
||||
TicketAddAnim();
|
||||
}).AddTo(disposables);
|
||||
|
||||
// panel_gift3.gameObject.SetActive(false);
|
||||
#if UNITY_IOS || UNITY_IPHONE
|
||||
panel_gift2?.gameObject.SetActive(true);
|
||||
panel_gift3?.gameObject.SetActive(false);
|
||||
#else
|
||||
CheckTapJoyOpen();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// 7天
|
||||
private readonly int _conditionSeconds = 604800; // 7 * 24 * 60 * 60;
|
||||
|
||||
// 判断是否满足{seconds}没有充值了
|
||||
private bool Condition_NoRechargeSeconds(int timeSeconds)
|
||||
{
|
||||
var lastPaymentStr = saveStore.Read("LastPaymentTime").ValueOrThrow();
|
||||
DateTime curTime = ZZTimeHelper.UtcNow().UtcNowOffset();
|
||||
DateTime endTime = curTime;
|
||||
if (string.IsNullOrEmpty(lastPaymentStr) || !lastPaymentStr.Contains("__"))
|
||||
endTime = GContext.container.Resolve<IUserService>().CreateTime.UtcNowOffset();
|
||||
|
||||
var curTimeSecond = new DateTimeOffset(curTime).ToUnixTimeSeconds();
|
||||
var endTimeSecond = new DateTimeOffset(endTime).ToUnixTimeSeconds();
|
||||
if (curTimeSecond - endTimeSecond >= timeSeconds)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CheckTapJoyOpen()
|
||||
{
|
||||
var tables = GContext.container.Resolve<Tables>();
|
||||
var tapjoyWakeParam = tables.TbGlobalConfig.TapjoyWakeParam * 24 * 60 * 60;
|
||||
var config = GContext.container.Resolve<IConfig>();
|
||||
// bool isTapJoyOpen = !(config != null && string.IsNullOrEmpty(config.Get(GConstant.K_TAPJOY, string.Empty)));
|
||||
bool isTapJoyOpen = config == null || string.IsNullOrEmpty(config.Get(GConstant.K_TAPJOY_BAN, string.Empty)) ;
|
||||
|
||||
#if UNITY_ANDROID
|
||||
if (Condition_NoRechargeSeconds(tapjoyWakeParam) && isTapJoyOpen)
|
||||
{
|
||||
panel_gift2?.gameObject.SetActive(false);
|
||||
panel_gift3?.gameObject.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
panel_gift2?.gameObject.SetActive(true);
|
||||
panel_gift3?.gameObject.SetActive(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
//private async System.Threading.Tasks.Task WaitForRewardPanel()
|
||||
//{
|
||||
// await new WaitForSeconds(0.5f);
|
||||
// var rewardPanelTask = new RewardPopupData();
|
||||
// GContext.Publish(rewardPanelTask);
|
||||
// if (rewardPanelTask.tcs != null)
|
||||
// {
|
||||
// await rewardPanelTask.tcs.Task;
|
||||
// }
|
||||
|
||||
//}
|
||||
void GoldAddAnim()
|
||||
{
|
||||
//await WaitForRewardPanel();
|
||||
DOTween.Kill("txt_money");
|
||||
txt_money.text = ConvertTools.GetNumberString2(_curGold);
|
||||
var EndDiamond = GContext.container.Resolve<PlayerData>().gold;
|
||||
DOTween.To(() => _curGold, x => _curGold = x, EndDiamond, 1f).OnUpdate(() =>
|
||||
{
|
||||
txt_money.text = ConvertTools.GetNumberString2(_curGold);
|
||||
}).SetId("txt_money");
|
||||
}
|
||||
void EnergyAddAnim()
|
||||
{
|
||||
//await WaitForRewardPanel();
|
||||
DOTween.Kill("advert_text_energy");
|
||||
txt_energy.text = ConvertTools.GetNumberString2(_curEnergy);
|
||||
var EndEnergy = GContext.container.Resolve<PlayerData>().Energy;
|
||||
DOTween.To(() => _curEnergy, x => _curEnergy = x, EndEnergy, 1f).OnUpdate(() =>
|
||||
{
|
||||
txt_energy.text = ConvertTools.GetNumberString2(_curEnergy);
|
||||
}).SetId("advert_text_energy");
|
||||
}
|
||||
void TicketAddAnim()
|
||||
{
|
||||
//await WaitForRewardPanel();
|
||||
DOTween.Kill("text_ticket");
|
||||
txt_ticket.text = ConvertTools.GetNumberString2(_curTicket);
|
||||
var Ticket = GContext.container.Resolve<PlayerShopData>().GetAdticketCount();
|
||||
DOTween.To(() => _curTicket, x => _curTicket = x, Ticket, 1f).OnUpdate(() =>
|
||||
{
|
||||
txt_ticket.text = ConvertTools.GetNumberString2(_curTicket);
|
||||
}).SetId("text_ticket");
|
||||
}
|
||||
private void OnDestroy()
|
||||
{
|
||||
disposables.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,674 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameCore;
|
||||
using asap.core;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using cfg;
|
||||
using IngameDebugConsole;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using game;
|
||||
using System.Linq;
|
||||
using Activity.Event;
|
||||
|
||||
public class DeBugPanel : MonoBehaviour
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
static public bool isShow = true;
|
||||
private CanvasGroup _canvasGroup;
|
||||
private Button btn_close;
|
||||
private Button root_review_closs;
|
||||
private Button btn_changjing;
|
||||
private Button btn_yugan;
|
||||
//private Button btn_sure;
|
||||
private Button btn_sure1;
|
||||
|
||||
TMP_Text text_cur_time;
|
||||
|
||||
TMP_Text text_cur_viplv;
|
||||
TMP_Text text_cur_pricelv;
|
||||
public TMP_InputField inputField_vip;
|
||||
public Button btn_vip;
|
||||
TMP_InputField inputField_Seconds;
|
||||
Button btn_Day;
|
||||
Button btn_Seconds;
|
||||
TMP_InputField inputField_Digging;
|
||||
Button btn_Digging;
|
||||
|
||||
TMP_InputField inputField_duel;
|
||||
Button btn_duel;
|
||||
private TMP_InputField inputField_yu;
|
||||
//private TMP_InputField inputField_grade;
|
||||
|
||||
TMP_InputField inputField_itemId;
|
||||
TMP_InputField inputField_itemCount;
|
||||
|
||||
Button btn_addItem;
|
||||
|
||||
//账号等级
|
||||
TMP_InputField inputField_level;
|
||||
|
||||
Button btn_level;
|
||||
|
||||
//账号等级
|
||||
TMP_InputField inputField_supplyDrop;
|
||||
|
||||
Button btn_supplyDrop;
|
||||
|
||||
//活动ID
|
||||
TMP_InputField inputField_activityId;
|
||||
Button btn_activityId;
|
||||
|
||||
//鱼竿
|
||||
public
|
||||
TMP_InputField inputField_rod;
|
||||
public
|
||||
Button btn_rodFull;
|
||||
public
|
||||
Button btn_rod_full_star;
|
||||
//鱼熟练度全升级
|
||||
Button btn_fishFull;
|
||||
//鱼熟练度全满
|
||||
Button btn_fishFull1;
|
||||
//鱼熟练度全升级
|
||||
Button btn_fishFullAll;
|
||||
Button btn_fishFullAll1;
|
||||
//图鉴全满
|
||||
Button btn_albumFull;
|
||||
Button btn_show_panel;
|
||||
|
||||
TMP_InputField inputField_guide;
|
||||
Button btn_guide;
|
||||
|
||||
TMP_InputField inputField_build;
|
||||
Button btn_build;
|
||||
|
||||
Button btn_bomb;
|
||||
TMP_InputField inputField_btn_bomb;
|
||||
|
||||
Button btn_aquarium;
|
||||
Button btn_raid;
|
||||
Button btn_bomb_game;
|
||||
|
||||
private GameObject root_review;
|
||||
private GameObject root;
|
||||
|
||||
private TMP_Text text_title;
|
||||
|
||||
private Transform content;
|
||||
|
||||
GameObject item;
|
||||
|
||||
List<DeBugItem> deBugItems = new List<DeBugItem>();
|
||||
|
||||
int _index = 0;
|
||||
Button btn_album_orientation;
|
||||
Button btn_album_orientation1;
|
||||
Button btn_console_toggle;
|
||||
public TMP_InputField inputField_mmt;
|
||||
public Button btn_mmt;
|
||||
float inflate = 0f;
|
||||
// Start is called before the first frame update
|
||||
void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
inputField_itemId = transform.Find("root/InputField_icon").GetComponent<TMP_InputField>();
|
||||
inputField_itemCount = transform.Find("root/InputField_icon_num").GetComponent<TMP_InputField>();
|
||||
btn_addItem = transform.Find("root/btn_sure2/btn_green").GetComponent<Button>();
|
||||
inputField_level = transform.Find("root/InputField_account_grade").GetComponent<TMP_InputField>();
|
||||
btn_level = transform.Find("root/btn_sure3/btn_green").GetComponent<Button>();
|
||||
|
||||
inputField_supplyDrop = transform.Find("root/InputField_supplyDropIndex").GetComponent<TMP_InputField>();
|
||||
btn_supplyDrop = transform.Find("root/btn_suresupplyDrop/btn_green").GetComponent<Button>();
|
||||
|
||||
inputField_activityId = transform.Find("root/InputField_event").GetComponent<TMP_InputField>();
|
||||
btn_activityId = transform.Find("root/btn_sure4/btn_green").GetComponent<Button>();
|
||||
btn_fishFull = transform.Find("root/btn_fish_upgrade1/btn_green").GetComponent<Button>();
|
||||
btn_fishFull1 = transform.Find("root/btn_fish_full1/btn_green").GetComponent<Button>();
|
||||
btn_fishFullAll = transform.Find("root/btn_fish_upgrade2/btn_green").GetComponent<Button>();
|
||||
btn_fishFullAll1 = transform.Find("root/btn_fish_full2/btn_green").GetComponent<Button>();
|
||||
btn_albumFull = transform.Find("root/btn_album_full/btn_green").GetComponent<Button>();
|
||||
btn_show_panel = transform.Find("root/btn_show_panel/btn_green").GetComponent<Button>();
|
||||
inputField_guide = transform.Find("root/InputField_guide").GetComponent<TMP_InputField>();
|
||||
btn_guide = transform.Find("root/btn_guide/btn_green").GetComponent<Button>();
|
||||
inputField_build = transform.Find("root/InputField_build").GetComponent<TMP_InputField>();
|
||||
btn_build = transform.Find("root/btn_build/btn_green").GetComponent<Button>();
|
||||
btn_bomb = transform.Find("root/btn_bomb/btn_green").GetComponent<Button>();
|
||||
inputField_btn_bomb = transform.Find("root/InputField_bomb").GetComponent<TMP_InputField>();
|
||||
|
||||
btn_aquarium = transform.Find("root/btn_aquarium/btn_green").GetComponent<Button>();
|
||||
btn_raid = transform.Find("root/btn_raid/btn_green").GetComponent<Button>();
|
||||
btn_bomb_game = transform.Find("root/btn_bomb_game/btn_green").GetComponent<Button>();
|
||||
_canvasGroup = GetComponent<CanvasGroup>();
|
||||
root = transform.Find("root").gameObject;
|
||||
btn_close = transform.Find("root/btn_close").GetComponent<Button>();
|
||||
root_review_closs = transform.Find("root_review/btn_close").GetComponent<Button>();
|
||||
btn_changjing = transform.Find("root/btn_changjing/btn_green").GetComponent<Button>();
|
||||
btn_yugan = transform.Find("root/btn_yugan/btn_green").GetComponent<Button>();
|
||||
//btn_sure = transform.Find("root/btn_sure/btn_green").GetComponent<Button>();
|
||||
btn_sure1 = transform.Find("root/btn_sure1/btn_green").GetComponent<Button>();
|
||||
inputField_yu = transform.Find("root/InputField_yu").GetComponent<TMP_InputField>();
|
||||
//inputField_grade = transform.Find("root/InputField_grade").GetComponent<TMP_InputField>();
|
||||
root_review = transform.Find("root_review").gameObject;
|
||||
text_title = transform.Find("root_review/text_title").GetComponent<TMP_Text>();
|
||||
content = transform.Find("root_review/Scroll View/Viewport/Content");
|
||||
item = transform.Find("root_review/Scroll View/Viewport/Content/Item").gameObject;
|
||||
btn_album_orientation = transform.Find("root/btn_album_orientation/btn_green").GetComponent<Button>();
|
||||
btn_album_orientation1 = transform.Find("root/btn_album_orientation1/btn_green").GetComponent<Button>();
|
||||
btn_console_toggle = transform.Find("root/Console/btn_green").GetComponent<Button>();
|
||||
SetSecondsTime();
|
||||
SetDigging();
|
||||
SetDuelTier();
|
||||
SetVIP();
|
||||
SetMMT();
|
||||
|
||||
inflate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
}
|
||||
void SetMMT()
|
||||
{
|
||||
inputField_mmt.text = GContext.container.Resolve<MMTService>().MMTStr;
|
||||
btn_mmt.onClick.AddListener(ChangeMMT);
|
||||
}
|
||||
void SetVIP()
|
||||
{
|
||||
text_cur_viplv = transform.Find("root/text_cur_viplv").GetComponent<TMP_Text>();
|
||||
text_cur_pricelv = transform.Find("root/text_cur_pricelv").GetComponent<TMP_Text>();
|
||||
btn_vip.onClick.AddListener(SetPriceLv);
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
var _tables = GContext.container.Resolve<Tables>();
|
||||
var dynamicVipParam = _tables.TbGlobalConfig.DynamicVipParam;
|
||||
int allPackPoints = (int)(playerData.vipAllScore * dynamicVipParam[0] + playerData.vipLastScore * dynamicVipParam[1]);
|
||||
text_cur_viplv.text = $"{playerData.vip}({playerData.vipAllScore})";
|
||||
text_cur_pricelv.text = $"{playerData.PriceLv}({allPackPoints})";
|
||||
}
|
||||
|
||||
void SetPriceLv()
|
||||
{
|
||||
string secStr = inputField_vip.text;
|
||||
if (int.TryParse(secStr, out int sec))
|
||||
{
|
||||
PlayerData playerData = GContext.container.Resolve<PlayerData>();
|
||||
playerData.SetPriceLevel(sec);
|
||||
var _tables = GContext.container.Resolve<Tables>();
|
||||
var dynamicVipParam = _tables.TbGlobalConfig.DynamicVipParam;
|
||||
int allPackPoints = (int)(playerData.vipAllScore * dynamicVipParam[0] + playerData.vipLastScore * dynamicVipParam[1]);
|
||||
text_cur_viplv.text = $"{playerData.vip}({playerData.vipAllScore})";
|
||||
text_cur_pricelv.text = $"{playerData.PriceLv}({allPackPoints})";
|
||||
}
|
||||
}
|
||||
|
||||
void SetSecondsTime()
|
||||
{
|
||||
text_cur_time = transform.Find("root/text_cur_time").GetComponent<TMP_Text>();
|
||||
inputField_Seconds = transform.Find("root/InputField_Seconds").GetComponent<TMP_InputField>();
|
||||
inputField_Seconds.text = ZZTimeHelper.LocalTimeOffsetInSec.ToString();
|
||||
btn_Day = transform.Find("root/btn_Day/btn_green").GetComponent<Button>();
|
||||
btn_Seconds = transform.Find("root/btn_Seconds/btn_green").GetComponent<Button>();
|
||||
|
||||
text_cur_time.text = ZZTimeHelper.UtcNow().ToString("yyyy/MM/dd HH:mm:ss");
|
||||
btn_Seconds.onClick.AddListener(OnClickSetTime);
|
||||
btn_Day.onClick.AddListener(OnClickSetTimeDay);
|
||||
}
|
||||
|
||||
void OnClickSetTime()
|
||||
{
|
||||
string secStr = inputField_Seconds.text;
|
||||
if (!string.IsNullOrEmpty(secStr))
|
||||
{
|
||||
if (int.TryParse(secStr, out int sec))
|
||||
{
|
||||
ZZTimeHelper.LocalTimeOffsetInSec = sec;
|
||||
text_cur_time.text = ZZTimeHelper.UtcNow().ToString("yyyy/MM/dd HH:mm:ss");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("输入的秒数格式不正确");
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnClickSetTimeDay()
|
||||
{
|
||||
string secStr = inputField_Seconds.text;
|
||||
if (!string.IsNullOrEmpty(secStr))
|
||||
{
|
||||
if (int.TryParse(secStr, out int sec))
|
||||
{
|
||||
ZZTimeHelper.LocalTimeOffsetInSec = sec * 86400;
|
||||
text_cur_time.text = ZZTimeHelper.UtcNow().ToString("yyyy/MM/dd HH:mm:ss");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("输入的天数格式不正确");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetDigging()
|
||||
{
|
||||
inputField_Digging = transform.Find("root/InputField_Digging").GetComponent<TMP_InputField>();
|
||||
btn_Digging = transform.Find("root/btn_Digging/btn_green").GetComponent<Button>();
|
||||
btn_Digging.onClick.AddListener(OnClickDigging);
|
||||
}
|
||||
void OnClickDigging()
|
||||
{
|
||||
string indexStr = inputField_Digging.text;
|
||||
if (!string.IsNullOrEmpty(indexStr))
|
||||
{
|
||||
int.TryParse(indexStr, out int index);
|
||||
GContext.container.Resolve<Script.RuntimeScript.DiggingGameManager>().GMSetClamp(index);
|
||||
}
|
||||
}
|
||||
private void Start()
|
||||
{
|
||||
btn_close.onClick.AddListener(OnClosePanel);
|
||||
btn_changjing.onClick.AddListener(OnChangjing);
|
||||
btn_yugan.onClick.AddListener(OnYugan);
|
||||
//btn_sure.onClick.AddListener(OnSure);
|
||||
btn_sure1.onClick.AddListener(OnSure1);
|
||||
btn_addItem.onClick.AddListener(AddItem);
|
||||
btn_level.onClick.AddListener(SetLevel);
|
||||
|
||||
btn_supplyDrop.onClick.AddListener(SetSupplyDrop);
|
||||
|
||||
btn_activityId.onClick.AddListener(SetaCtivityId);
|
||||
btn_rodFull.onClick.AddListener(() =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_rod.text))
|
||||
{
|
||||
int level = Convert.ToInt32(inputField_rod.text);
|
||||
GContext.container.Resolve<PlayerFishData>().GMSetLvAll(level);
|
||||
}
|
||||
});
|
||||
btn_rod_full_star.onClick.AddListener(() =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_rod.text))
|
||||
{
|
||||
int level = Convert.ToInt32(inputField_rod.text);
|
||||
GContext.container.Resolve<PlayerFishData>().GMSetStarAll(level);
|
||||
}
|
||||
});
|
||||
btn_fishFull.onClick.AddListener(() =>
|
||||
{
|
||||
GContext.container.Resolve<PlayerFishData>().GMAddAllFishMastery();
|
||||
});
|
||||
btn_fishFull1.onClick.AddListener(() =>
|
||||
{
|
||||
GContext.container.Resolve<PlayerFishData>().GMAddAllFishMastery1();
|
||||
});
|
||||
btn_fishFullAll.onClick.AddListener(() =>
|
||||
{
|
||||
GContext.container.Resolve<PlayerFishData>().GMAddAllFishMastery(true);
|
||||
});
|
||||
btn_fishFullAll1.onClick.AddListener(() =>
|
||||
{
|
||||
GContext.container.Resolve<PlayerFishData>().GMAddAllFishMastery1(true);
|
||||
});
|
||||
btn_albumFull.onClick.AddListener(() =>
|
||||
{
|
||||
//GContext.container.Resolve<AlbumData>().GMAddAll();
|
||||
MMTService mmService = GContext.container.Resolve<MMTService>();
|
||||
mmService.CanShowHand = !mmService.CanShowHand;
|
||||
GContext.Publish(new ShowHandEvent() { isShow = mmService.CanShowHand });
|
||||
});
|
||||
btn_guide.onClick.AddListener(() =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_guide.text))
|
||||
{
|
||||
GContext.container.Resolve<GuideDataCenter>().ClearGuide(inputField_guide.text);
|
||||
OnClosePanel();
|
||||
}
|
||||
});
|
||||
btn_build.onClick.AddListener(() =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_build.text))
|
||||
{
|
||||
if (inputField_build.text.Contains("-"))
|
||||
{
|
||||
string[] strings = inputField_build.text.Split('-');
|
||||
GContext.container.Resolve<CampDataMM>().SetCampAreaIndex(strings[0]);
|
||||
GContext.container.Resolve<CampDataMM>().SetCampAreaStep(strings[1]);
|
||||
GContext.container.Resolve<CampDataMM>().UpdateCurrentData();
|
||||
}
|
||||
else
|
||||
{
|
||||
GContext.container.Resolve<CampDataMM>().ChangeCampId(int.Parse(inputField_build.text));
|
||||
}
|
||||
OnClosePanel();
|
||||
}
|
||||
});
|
||||
btn_bomb.onClick.AddListener(() =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_btn_bomb.text))
|
||||
{
|
||||
GContext.container.Resolve<SmallGameData>().GMSetBomb(inputField_btn_bomb.text);
|
||||
OnClosePanel();
|
||||
}
|
||||
});
|
||||
|
||||
root_review_closs.onClick.AddListener(() => SetRootReview(false));
|
||||
btn_show_panel.onClick.AddListener(ChangeShow);
|
||||
btn_album_orientation.onClick.AddListener(() =>
|
||||
{
|
||||
Screen.orientation = ScreenOrientation.LandscapeLeft;
|
||||
});
|
||||
btn_album_orientation1.onClick.AddListener(() =>
|
||||
{
|
||||
Screen.orientation = ScreenOrientation.Portrait;
|
||||
});
|
||||
|
||||
btn_console_toggle.onClick.AddListener(() =>
|
||||
{
|
||||
if (null == DebugLogManager.Instance)
|
||||
{
|
||||
Addressables.InstantiateAsync("IngameDebugConsole");
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(DebugLogManager.Instance.gameObject);
|
||||
}
|
||||
});
|
||||
btn_aquarium.onClick.AddListener(GMShootFishRefresh);
|
||||
btn_bomb_game.onClick.AddListener(async () =>
|
||||
{
|
||||
var eventGamePlayPanel = await UIManager.Instance.GetUIAsync(UITypes.EventGamePlayPanel);
|
||||
eventGamePlayPanel.GetComponent<EventGamePlayPanel>().StartGame(12);
|
||||
UIManager.Instance.DestroyUI(UITypes.DeBugPanel);
|
||||
});
|
||||
btn_raid.onClick.AddListener(async () =>
|
||||
{
|
||||
var eventGamePlayPanel = await UIManager.Instance.GetUIAsync(UITypes.EventGamePlayPanel);
|
||||
eventGamePlayPanel.GetComponent<EventGamePlayPanel>().StartGame(11);
|
||||
UIManager.Instance.DestroyUI(UITypes.DeBugPanel);
|
||||
});
|
||||
}
|
||||
// Update is called once per frame
|
||||
|
||||
|
||||
// public override void OnPause()
|
||||
// {
|
||||
// //当弹出新的面板的时候,让主菜单面板不再和鼠标交互
|
||||
// _canvasGroup.alpha = 0;
|
||||
// _canvasGroup.blocksRaycasts = false;
|
||||
// }
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
//当弹出新的面板的时候,让主菜单面板不再和鼠标交互
|
||||
_canvasGroup.alpha = 1;
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
|
||||
//UIManager.Instance.DestroyUI(UITypes.HomePanel);
|
||||
GContext.Publish(new HideHomePanelEvent());
|
||||
}
|
||||
|
||||
public void OnDisable()
|
||||
{
|
||||
// _canvasGroup.alpha = 0;
|
||||
// _canvasGroup.blocksRaycasts = false;
|
||||
|
||||
RestartShowHomeUIEvent restartShowHomeUIEvent = new RestartShowHomeUIEvent();
|
||||
GContext.Publish(restartShowHomeUIEvent);
|
||||
}
|
||||
|
||||
public void OnClosePanel()
|
||||
{
|
||||
UIManager.Instance.DestroyUI(UITypes.DeBugPanel);
|
||||
}
|
||||
|
||||
void SetRootReview(bool isShow, int index = 0)
|
||||
{
|
||||
_index = index;
|
||||
root_review.SetActive(isShow);
|
||||
root.SetActive(!isShow);
|
||||
}
|
||||
|
||||
void OnChangjing()
|
||||
{
|
||||
if (_index == 2)
|
||||
return;
|
||||
var list = GContext.container.Resolve<Tables>().TbMapData.DataList;
|
||||
int count = deBugItems.Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
deBugItems[i].gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
if (i >= count)
|
||||
{
|
||||
deBugItems.Add(Instantiate(item, content).AddComponent<DeBugItem>());
|
||||
}
|
||||
|
||||
var i1 = i;
|
||||
var map_name = LocalizationMgr.GetText(list[i].Name_l10n_key);
|
||||
deBugItems[i].deBugItemData = new DeBugItemData($"{map_name}\n{list[i].ID}",
|
||||
async () =>
|
||||
{
|
||||
ChangeMapDataAsyncEvent changeMapDataAsyncEvent = new ChangeMapDataAsyncEvent(list[i1].ID);
|
||||
GContext.Publish(changeMapDataAsyncEvent);
|
||||
await changeMapDataAsyncEvent.source.Task;
|
||||
});
|
||||
deBugItems[i].gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
SetRootReview(true, 2);
|
||||
}
|
||||
|
||||
void OnYugan()
|
||||
{
|
||||
if (_index == 1)
|
||||
return;
|
||||
var list = GContext.container.Resolve<Tables>().TbRodData.DataList;
|
||||
int count = deBugItems.Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
deBugItems[i].gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void OnSure1()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_yu.text))
|
||||
{
|
||||
int yu = Convert.ToInt32(inputField_yu.text);
|
||||
var fishItem = GContext.container.Resolve<Tables>().GetItemData(yu);
|
||||
if (fishItem != null && fishItem.RedirectID > 0 && fishItem.Type == 4)
|
||||
{
|
||||
GContext.container.Resolve<FishingData>().fishItem = fishItem;
|
||||
GContext.Publish(new ChangeFishDataEvent(fishItem.RedirectID));
|
||||
}
|
||||
else
|
||||
{
|
||||
ToastPanel.Show("输入的ID不是鱼");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddItem()
|
||||
{
|
||||
int.TryParse(inputField_itemCount.text, out int count);
|
||||
if (count == 0)
|
||||
{
|
||||
count = 1;
|
||||
}
|
||||
GContext.container.Resolve<PlayerItemData>().AddItemCount(Convert.ToInt32(inputField_itemId.text), count, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SetLevel()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_level.text))
|
||||
{
|
||||
int level = Convert.ToInt32(inputField_level.text);
|
||||
GContext.container.Resolve<PlayerData>().SetLv(level);
|
||||
GContext.container.Resolve<CampDataMM>().Init();
|
||||
}
|
||||
}
|
||||
|
||||
void SetSupplyDrop()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_supplyDrop.text))
|
||||
{
|
||||
int level = Convert.ToInt32(inputField_supplyDrop.text);
|
||||
GContext.container.Resolve<PlayerData>().SupplyDropIndex = level;
|
||||
}
|
||||
}
|
||||
|
||||
void SetaCtivityId()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(inputField_activityId.text))
|
||||
{
|
||||
int activityId = Convert.ToInt32(inputField_activityId.text);
|
||||
GContext.container.Resolve<FishingEventData>().GMSetEvent(activityId);
|
||||
var eventConfig=GContext.container.Resolve<Tables>()?.TbFishingEvent.GetOrDefault(activityId);
|
||||
GContext.Publish(new GMActivityOpenedEvent(eventConfig));
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeShow()
|
||||
{
|
||||
isShow = !isShow;
|
||||
CampPanel campPanel = GContext.container.Resolve<CampPanel>();
|
||||
if (campPanel != null)
|
||||
{
|
||||
campPanel.SetAlpha();
|
||||
}
|
||||
}
|
||||
|
||||
void SetDuelTier()
|
||||
{
|
||||
inputField_duel = transform.Find("root/InputField_duel").GetComponent<TMP_InputField>();
|
||||
btn_duel = transform.Find("root/btn_sure_duel/btn_green").GetComponent<Button>();
|
||||
btn_duel.onClick.AddListener(GMDuelSetTier);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// GM命令调段位
|
||||
/// </summary>
|
||||
/// <param name="initTier"></param>
|
||||
public void GMDuelSetTier()
|
||||
{
|
||||
string indexStr = inputField_duel.text;
|
||||
if (!string.IsNullOrEmpty(indexStr))
|
||||
{
|
||||
int.TryParse(indexStr, out int count);
|
||||
GContext.container.Resolve<FishingEventData>().GMAddSoloProgress(count);
|
||||
}
|
||||
}
|
||||
public void GMShootFishRefresh()
|
||||
{
|
||||
var dataSever = AquariumManager.GetAquariumManagerData();
|
||||
if (dataSever != null)
|
||||
{
|
||||
var data = dataSever;
|
||||
//新数据
|
||||
data.startTime = ZZTimeHelper.UtcNow().AddDays(-2);
|
||||
data.refreshCount = 0;
|
||||
data.adCount = 0;
|
||||
data.refreshDay = ZZTimeHelper.UtcNow().UtcNowOffset().Date.DayOfYear;
|
||||
saveStore.Write(AquariumManager.AquariumEventDataNewKey, data.Serialize()).RequireAccepted();
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeMMT()
|
||||
{
|
||||
var userService = GContext.container.Resolve<game.IUserService>() as game.PlayFabUserService;
|
||||
if (userService == null) return;
|
||||
string mmt = inputField_mmt.text;
|
||||
List<string> mmts = mmt.ToLower().Select(c => c.ToString()).ToList();
|
||||
var mmDatas = GContext.container.Resolve<Tables>().TbMMTInit.DataList;
|
||||
int value = 0;
|
||||
for (int i = 0; i < mmDatas.Count; i++)
|
||||
{
|
||||
if (mmts.Count > i && mmts[i] == "b")
|
||||
{
|
||||
value |= 1 << (mmDatas[i].ID - 1001);
|
||||
}
|
||||
}
|
||||
userService.GMMMT(value);
|
||||
}
|
||||
|
||||
private string _cfg_ver;
|
||||
private string CfgVer => _cfg_ver ??
|
||||
#if UNITY_EDITOR
|
||||
(_cfg_ver = $"Conf Ver [{System.IO.File.ReadAllText("../cfgData/conf_ver.txt").Trim()}]");
|
||||
#else
|
||||
(_cfg_ver = $"Conf Ver [{System.IO.File.ReadAllText(System.IO.Path.Combine(Application.persistentDataPath,"cfgData/conf_ver.txt")).Trim()}]");
|
||||
#endif
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
//draw cfg ver
|
||||
if (!_originFlag)
|
||||
{
|
||||
_originFlag = true;
|
||||
GetOriginalGUIStyle();
|
||||
}
|
||||
GUIStyle style = GUI.skin.label;
|
||||
style.fontSize = 80;
|
||||
style.normal.textColor = Color.magenta;
|
||||
GUI.Label(new Rect(50, 50, 2000, 100), CfgVer, style);
|
||||
GUILayout.Space(200);
|
||||
string inflateStr = $"inflate {inflate}";
|
||||
GUILayout.Label(inflateStr, style, GUILayout.Width(2000), GUILayout.Height(100));
|
||||
SetOriginalGUIStyle();
|
||||
}
|
||||
private GUIStyle _originalGUIStyle;
|
||||
private bool _originFlag = false;
|
||||
private void GetOriginalGUIStyle()
|
||||
{
|
||||
_originalGUIStyle = new GUIStyle(GUI.skin.label);
|
||||
}
|
||||
private void SetOriginalGUIStyle()
|
||||
{
|
||||
if (_originalGUIStyle == null)
|
||||
return;
|
||||
GUI.skin.GetStyle("Label").normal.textColor = _originalGUIStyle.normal.textColor;
|
||||
GUI.skin.GetStyle("Label").fontSize = _originalGUIStyle.fontSize;
|
||||
}
|
||||
}
|
||||
|
||||
class DeBugItem : MonoBehaviour
|
||||
{
|
||||
public TMP_Text text;
|
||||
public Button btn;
|
||||
public DeBugItemData deBugItemData;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
text = transform.Find("text").GetComponent<TMP_Text>();
|
||||
btn = transform.GetComponent<Button>();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
text.text = deBugItemData.str;
|
||||
btn.onClick.AddListener(() => deBugItemData.action());
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
btn.onClick.RemoveAllListeners();
|
||||
}
|
||||
}
|
||||
|
||||
class DeBugItemData
|
||||
{
|
||||
public string str;
|
||||
public Action action;
|
||||
|
||||
public DeBugItemData(string str, Action action)
|
||||
{
|
||||
this.str = str;
|
||||
this.action = action;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using tysdk;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityNative.Sharing;
|
||||
|
||||
public class EventInviteFriends : MonoBehaviour
|
||||
{
|
||||
private IUnityNativeSharing adapter;
|
||||
|
||||
Button btn_close;
|
||||
Button btn_mask;
|
||||
Button btn_questionmark;
|
||||
Image bar;
|
||||
GameObject scale;
|
||||
TMP_Text text_num;
|
||||
RewardItemNew bigReward;
|
||||
float rewardWidth;
|
||||
RewardItemNew[] smallReward;
|
||||
Button btn_invite_link;
|
||||
Button btn_invite_fb;
|
||||
string inviteLink;
|
||||
List<Invite> invites;
|
||||
int maxNum;
|
||||
int maxIndex;
|
||||
Tables tables;
|
||||
PlayerItemData playerItemData;
|
||||
FriendService friendService;
|
||||
private void Awake()
|
||||
{
|
||||
friendService = GContext.container.Resolve<FriendService>();
|
||||
playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
tables = GContext.container.Resolve<Tables>();
|
||||
invites = tables.TbInvite.DataList;
|
||||
maxIndex = invites.Count - 1;
|
||||
maxNum = invites[maxIndex].NumInvite;
|
||||
btn_close = transform.Find("btn_close").GetComponent<Button>();
|
||||
btn_mask = transform.Find("mask").GetComponent<Button>();
|
||||
btn_questionmark = transform.Find("root/btn_questionmark").GetComponent<Button>();
|
||||
bar = transform.Find("root/bg_bar/bar").GetComponent<Image>();
|
||||
scale = transform.Find("root/bg_bar/bar/scale").gameObject;
|
||||
text_num = transform.Find("root/bg_done/text_num").GetComponent<TMP_Text>();
|
||||
bigReward = transform.Find("root/bigreward/reward").GetComponent<RewardItemNew>();
|
||||
Transform rewardRoot = transform.Find("root/reward");
|
||||
rewardWidth = rewardRoot.GetComponent<RectTransform>().rect.width;
|
||||
smallReward = rewardRoot.GetComponentsInChildren<RewardItemNew>();
|
||||
btn_invite_link = transform.Find("root/btn_invite_link/btn_green").GetComponent<Button>();
|
||||
btn_invite_fb = transform.Find("root/btn_invite_link/btn_green_fb").GetComponent<Button>();
|
||||
}
|
||||
private async void Start()
|
||||
{
|
||||
btn_close.onClick.AddListener(OnClickClose);
|
||||
btn_mask.onClick.AddListener(OnClickClose);
|
||||
btn_questionmark.onClick.AddListener(OnClickQuestionmark);
|
||||
btn_invite_link.onClick.AddListener(OnClickInviteLink);
|
||||
btn_invite_fb.onClick.AddListener(OnClickInviteFB);
|
||||
btn_invite_fb.gameObject.SetActive(!ChannelManager.IsRustoreStore);
|
||||
|
||||
int InviteRewardCount;
|
||||
try { InviteRewardCount = await friendService.OnReceiveLinkAward(); }
|
||||
catch (System.Exception e) { Debug.LogException(e); return; }
|
||||
if (this == null || !friendService.IsCurrent) return;
|
||||
|
||||
for (int i = 1; i < maxNum; i++)
|
||||
{
|
||||
var go = Instantiate(scale, bar.transform);
|
||||
go.transform.localPosition = new Vector3((i / (float)maxNum - 0.5f) * rewardWidth, 0, 0);
|
||||
}
|
||||
for (int i = 0; i < maxIndex; i++)
|
||||
{
|
||||
if (i < smallReward.Length)
|
||||
{
|
||||
if (string.IsNullOrEmpty(invites[i].GiftIcon))
|
||||
{
|
||||
var dataItem = playerItemData.GetItemDataOne(invites[i].DropID);
|
||||
if (dataItem != null)
|
||||
{
|
||||
smallReward[i].SetData(dataItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
smallReward[i].SetDropIcon(invites[i].GiftIcon, invites[i].DropID);
|
||||
}
|
||||
smallReward[i].SetReceived(i < InviteRewardCount);
|
||||
smallReward[i].transform.localPosition =
|
||||
new Vector3((invites[i].NumInvite / (float)maxNum - 0.5f) * rewardWidth, 0, 0);
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(invites[maxIndex].GiftIcon))
|
||||
{
|
||||
var dataItem = playerItemData.GetItemDataOne(invites[maxIndex].DropID);
|
||||
if (dataItem != null)
|
||||
{
|
||||
bigReward.SetData(dataItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bigReward.SetDropIcon(invites[maxIndex].GiftIcon, invites[maxIndex].DropID);
|
||||
}
|
||||
bigReward.SetReceived(maxIndex < InviteRewardCount);
|
||||
int InviteNumber = friendService.InviteLinkNumber;
|
||||
if (InviteNumber > maxNum)
|
||||
{
|
||||
InviteNumber = maxNum;
|
||||
}
|
||||
bar.fillAmount = InviteNumber / (float)maxNum;
|
||||
text_num.text = InviteNumber + "/" + maxNum;
|
||||
GenInviteLink();
|
||||
}
|
||||
async void GenInviteLink()
|
||||
{
|
||||
inviteLink = "";
|
||||
IUserService userService = GContext.container.Resolve<IUserService>();
|
||||
|
||||
var link = await AFHelper.Instance.GenInviteLink(userService.CustomId, userService.UserId);
|
||||
if (this != null && friendService.IsCurrent) inviteLink = link;
|
||||
}
|
||||
void OnClickClose()
|
||||
{
|
||||
UIManager.Instance.DestroyUI(UITypes.FishingSocialInviteFriendsPopupPanel);
|
||||
}
|
||||
void OnClickQuestionmark()
|
||||
{
|
||||
_ = UIManager.Instance.ShowUI(UITypes.FishingSocialInviteFriendsInfoPopupPanel);
|
||||
}
|
||||
void OnClickInviteLink()
|
||||
{
|
||||
if (string.IsNullOrEmpty(inviteLink))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (adapter == null)
|
||||
{
|
||||
adapter = UnityNativeSharing.Create();
|
||||
}
|
||||
string invite = LocalizationMgr.GetFormatTextValue("Invite_Link", inviteLink);
|
||||
adapter.ShareText(invite);
|
||||
}
|
||||
void OnClickInviteFB()
|
||||
{
|
||||
if (string.IsNullOrEmpty(inviteLink))
|
||||
{
|
||||
return;
|
||||
}
|
||||
string FBInvite_title = LocalizationMgr.GetText("FBInvite_title");
|
||||
string FBInvite_content = LocalizationMgr.GetText("FBInvite_content");
|
||||
UnityBridgeFunc.FBShareLink(FBInvite_title, FBInvite_content, inviteLink);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
using System;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.UI.JigsawPuzzle
|
||||
{
|
||||
public class JigsawPuzzleManager:IDisposable
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public const string PriceImgName = "separate_icon_eventjigsawpuzzle_piece_";
|
||||
|
||||
public JigsawPuzzleManager()
|
||||
{
|
||||
GContext.OnEvent<TargetEvent>().Subscribe(OnEvent_TargetEvent).AddTo(Disposables);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposables?.Dispose();
|
||||
_rewards?.Clear();
|
||||
}
|
||||
|
||||
private CompositeDisposable _disposable;
|
||||
private CompositeDisposable Disposables => _disposable ??= new CompositeDisposable();
|
||||
|
||||
#region event
|
||||
|
||||
private void OnEvent_TargetEvent(TargetEvent targetEvent)
|
||||
{
|
||||
if (targetEvent.type == 4 && targetEvent.subType == 9)
|
||||
{
|
||||
RefreshData(targetEvent.id);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion event
|
||||
|
||||
#region data
|
||||
|
||||
[Inject]
|
||||
public Tables Tables { get; set; }
|
||||
|
||||
private JigsawPuzzleData _jigsawPuzzleData;
|
||||
public JigsawPuzzleData Data => _jigsawPuzzleData ??= new JigsawPuzzleData(Tables);
|
||||
|
||||
private void RefreshData(int eventId)
|
||||
{
|
||||
//判断周开启时间
|
||||
FishingEvent fishingEvent = Tables.TbFishingEvent.GetOrDefault(eventId);
|
||||
if (fishingEvent == null) return;
|
||||
var CacheData = GetCacheData();
|
||||
if (CacheData != null && CacheData.EventID == eventId)
|
||||
{
|
||||
this.Data.SetData(CacheData);
|
||||
SetRedPoint();
|
||||
return;
|
||||
}
|
||||
InitData(eventId);
|
||||
SaveData();
|
||||
}
|
||||
private void InitData(int eventId)
|
||||
{
|
||||
this.Data.EventID = eventId;
|
||||
Data.CompletedRoundCount = 0;
|
||||
Data.PieceRepeatAmount = 0;
|
||||
this.Data.RefreshCache();
|
||||
Data.FixedInitList?.Clear();
|
||||
this.Data.InflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
var welcomeGift= GContext.container.Resolve<FishingEventData>().GetInitWelcomeGift(eventId);
|
||||
Debug.Log($"jsd InitData {welcomeGift}");
|
||||
AddToken(welcomeGift);
|
||||
}
|
||||
private JigsawPuzzleData GetCacheData()
|
||||
{
|
||||
string dataStr = saveStore.Read(nameof(JigsawPuzzleData)).ValueOrThrow();
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd JigsawPuzzleData LoadData sData {dataStr}");
|
||||
#endif
|
||||
if (dataStr == null)
|
||||
return null;
|
||||
|
||||
return JsonConvert.DeserializeObject<JigsawPuzzleData>(dataStr) ?? throw new JsonSerializationException("Null JigsawPuzzleData save.");
|
||||
}
|
||||
public void SaveData()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd JigsawPuzzleData SaveData {JsonConvert.SerializeObject(Data)}");
|
||||
#endif
|
||||
saveStore.Write(nameof(JigsawPuzzleData), JsonConvert.SerializeObject(Data)).RequireAccepted();
|
||||
}
|
||||
|
||||
private List<EventJigsawPuzzleReward> _rewards = new List<EventJigsawPuzzleReward>();
|
||||
|
||||
public List<EventJigsawPuzzleReward> GetEventRewards()
|
||||
{
|
||||
if (Data == null) return null;
|
||||
|
||||
if (_rewards == null)
|
||||
_rewards = new List<EventJigsawPuzzleReward>();
|
||||
else
|
||||
_rewards.Clear();
|
||||
|
||||
var round = Data.RoundConfig;
|
||||
if (round == null) return null;
|
||||
|
||||
foreach (var id in round.PuzzleRewardList)
|
||||
{
|
||||
var rewardConfig = Tables.TbEventJigsawPuzzleReward.Get(id);
|
||||
if (rewardConfig == null) continue;
|
||||
if (_rewards.Contains(rewardConfig)) continue;
|
||||
_rewards.Add(rewardConfig);
|
||||
}
|
||||
return _rewards;
|
||||
}
|
||||
|
||||
public EventJigsawPuzzleReward GetRandomReward()
|
||||
{
|
||||
if (Data.FixedInitList.Count < Data.EventMain.FixedInitList.Count)
|
||||
{
|
||||
var rewardID = Data.EventMain.FixedInitList[Data.FixedInitList.Count];
|
||||
if (rewardID != 0)
|
||||
{
|
||||
Data.FixedInitList.Add(rewardID);
|
||||
SaveData();
|
||||
var reward = _rewards.Find(x => x.Index == rewardID);
|
||||
if(reward==null)
|
||||
Debug.LogError($"get fixed reward rewardID:{rewardID} reward is null");
|
||||
return reward;
|
||||
}
|
||||
}
|
||||
|
||||
var progressConfig = GetProgressConfig(Data.CompletedPicCount);
|
||||
if (progressConfig == null)
|
||||
{
|
||||
Debug.LogError("progressConfig error");
|
||||
return null;
|
||||
}
|
||||
|
||||
bool isPuzzlePiece = UnityEngine.Random.Range(0, 101) < progressConfig.NotPieceProb * 100;
|
||||
|
||||
if (isPuzzlePiece)
|
||||
return GetRandomItemRewardIndex();
|
||||
|
||||
bool isRepeatPuzzle = UnityEngine.Random.Range(0, 101) < progressConfig.RepeatProb * 100;
|
||||
if (isRepeatPuzzle)
|
||||
return GetRandomRepeatPuzzleRewardIndex();
|
||||
return GetRandomNewPuzzleRewardIndex();
|
||||
}
|
||||
|
||||
private EventJigsawPuzzleExProb GetProgressConfig(int completedCount)
|
||||
{
|
||||
foreach (var probId in Data.RoundConfig.ProbList)
|
||||
{
|
||||
var config = Tables.TbEventJigsawPuzzleExProb.GetOrDefault(probId);
|
||||
if (config == null)
|
||||
{
|
||||
Debug.LogError($"配置错误 probId:{probId} 没有对应的config ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.CompletedPicCount == completedCount)
|
||||
return config;
|
||||
}
|
||||
Debug.LogError($"RoundConfig {Data.RoundConfig.ID} 没有完成这个 {completedCount}数量 的奖励 配置错误");
|
||||
return null;
|
||||
}
|
||||
|
||||
private EventJigsawPuzzleReward GetRandomRepeatPuzzleRewardIndex()
|
||||
{
|
||||
var puzzleRewards = _rewards.Where(x => x.ItemDropID == 0).ToList();
|
||||
var ownedPuzzles = puzzleRewards.Where(r => Data.ProgressPairs.ContainsKey(r.PictureID)
|
||||
&& Data.ProgressPairs[r.PictureID].Count == 6)
|
||||
.ToList();
|
||||
|
||||
if (ownedPuzzles.Count == 0)
|
||||
return GetWeightedRandomReward(puzzleRewards);
|
||||
|
||||
return GetWeightedRandomReward(ownedPuzzles);
|
||||
}
|
||||
|
||||
private EventJigsawPuzzleReward GetRandomNewPuzzleRewardIndex()
|
||||
{
|
||||
var puzzleRewards = _rewards.Where(x => x.ItemDropID == 0).ToList();
|
||||
var newPuzzles = puzzleRewards.Where(r => Data.ProgressPairs.ContainsKey(r.PictureID)
|
||||
&& Data.ProgressPairs[r.PictureID].Count < 6).ToList();
|
||||
|
||||
if (newPuzzles.Count == 0)
|
||||
return GetRandomRepeatPuzzleRewardIndex();
|
||||
|
||||
return GetWeightedRandomReward(newPuzzles);
|
||||
}
|
||||
|
||||
private EventJigsawPuzzleReward GetRandomItemRewardIndex()
|
||||
{
|
||||
var itemRewards = _rewards.Where(r => r.ItemDropID > 0).ToList();
|
||||
|
||||
if (itemRewards.Count == 0)
|
||||
return GetWeightedRandomReward(_rewards);
|
||||
|
||||
return GetWeightedRandomReward(itemRewards);
|
||||
}
|
||||
|
||||
private EventJigsawPuzzleReward GetWeightedRandomReward(List<EventJigsawPuzzleReward> availableRewards)
|
||||
{
|
||||
if (availableRewards.Count == 0)
|
||||
{
|
||||
Debug.LogError($"get random reward error {availableRewards.Count} ");
|
||||
return null;
|
||||
}
|
||||
|
||||
int totalWeight = availableRewards.Sum(r => r.Weight);
|
||||
|
||||
if (totalWeight <= 0)
|
||||
{
|
||||
Debug.LogError("总权重为0");
|
||||
return null;
|
||||
}
|
||||
|
||||
int randomValue = UnityEngine.Random.Range(0, totalWeight);
|
||||
int currentWeight = 0;
|
||||
|
||||
for (int i = 0; i < availableRewards.Count; i++)
|
||||
{
|
||||
currentWeight += availableRewards[i].Weight;
|
||||
if (randomValue < currentWeight)
|
||||
{
|
||||
return availableRewards[i];
|
||||
}
|
||||
}
|
||||
|
||||
return availableRewards.LastOrDefault();
|
||||
}
|
||||
|
||||
#endregion data
|
||||
|
||||
#region config
|
||||
|
||||
public int GetRewardDropID(int pictureID)
|
||||
{
|
||||
var index = Data.RoundConfig.PictureList.IndexOf(pictureID);
|
||||
if (index >= 0 && index < Data.RoundConfig.PictureRewardList.Count)
|
||||
return Data.RoundConfig.PictureRewardList[index];
|
||||
Debug.LogError($"{this.GetType().Name} 配置错误 puzzlePicture is null PictureID {pictureID}");
|
||||
return 0;
|
||||
}
|
||||
public int GetPictureIDByQuality(int quality)
|
||||
{
|
||||
var index = quality - 1;
|
||||
if(index >= Data.RoundConfig.PictureList.Count|| index < 0)
|
||||
return -1;
|
||||
return Data.RoundConfig.PictureList[index];
|
||||
}
|
||||
public string GetPictureNameByQuality(int quality)
|
||||
{
|
||||
return Tables.TbEventJigsawPuzzlePicture.GetOrDefault(GetPictureIDByQuality(quality))?.PictureName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region token
|
||||
|
||||
public void AddToken(int count)
|
||||
{
|
||||
var currentCount = GetTokenCount();
|
||||
if (Data == null) return;
|
||||
var totalCount = currentCount + count;
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(Data.EventID, totalCount);
|
||||
SetRedPoint();
|
||||
}
|
||||
|
||||
public void RemoveToken(int count)
|
||||
{
|
||||
if (Data == null) return;
|
||||
var eventData = GContext.container.Resolve<FishingEventData>();
|
||||
if (eventData == null) return;
|
||||
var current = eventData.GetTransitionDataCount(Data.EventID);
|
||||
var resCount = Mathf.Max(0, current - count);
|
||||
eventData.SaveTransitionData(Data.EventID, resCount);
|
||||
SetRedPoint();
|
||||
}
|
||||
|
||||
public int GetTokenCount()
|
||||
{
|
||||
if (Data == null) return 0;
|
||||
var eventData = GContext.container.Resolve<FishingEventData>();
|
||||
return eventData?.GetTransitionDataCount(Data.EventID) ?? 0;
|
||||
}
|
||||
|
||||
public bool IsCompleteAllPuzzle()
|
||||
{
|
||||
return Data.CompletedPicCount >= Data.RoundConfig.PictureList.Count;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region Red
|
||||
|
||||
private string GetRedPointKey()
|
||||
{
|
||||
return "eventJigsawPuzzle.red";
|
||||
}
|
||||
private bool GetRedPointState()
|
||||
{
|
||||
return GetTokenCount()>0;
|
||||
}
|
||||
private void SetRedPoint()
|
||||
{
|
||||
RedPointManager.Instance?.SetRedPointState(GetRedPointKey(), GetRedPointState(),GetTokenCount());
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
using System.Text;
|
||||
using GameCore;
|
||||
using cfg;
|
||||
using asap.core;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
|
||||
namespace OfferStoryChain
|
||||
{
|
||||
public class Manager
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
#region RedPoint
|
||||
public bool DoNeedRedPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
var packIdList = GetPackIdList();
|
||||
if (_roundCount >= packIdList.Count)
|
||||
return false;
|
||||
var packList = GetPackList(packIdList[_roundCount]);
|
||||
if (_progress < 0 || _progress >= packList.Count)
|
||||
return false;
|
||||
return packList[_progress].IAPID == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public const string RedPointKey = "offerstorychain";
|
||||
#endregion
|
||||
|
||||
|
||||
#region DataStorage
|
||||
private int _eventId, _progress, _vipOnTrigger, _roundCount;
|
||||
public int EventId => _eventId;
|
||||
public int Progress => _progress;
|
||||
public int VipLevel => _vipOnTrigger;
|
||||
public int RoundCount => _roundCount;
|
||||
public bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
(var startTime, var endTime) = FtUtils.GetEventTimeLimit(_eventId);
|
||||
var isWithinTime = ZZTimeHelper.UtcNow() >= startTime && ZZTimeHelper.UtcNow() <= endTime;
|
||||
var isOnGoing = _roundCount < GetMaxRoundCount();
|
||||
return isWithinTime && isOnGoing;
|
||||
}
|
||||
}
|
||||
|
||||
private const string PlayFabKey = "OfferStoryChain";
|
||||
private PlayerItemData _playerItemData => GContext.container.Resolve<PlayerItemData>();
|
||||
|
||||
public static void InitEvent(cfg.FishingEvent e)
|
||||
{
|
||||
var manager = GContext.container.Resolve<Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
GContext.container.Register<Manager>().AsSingleton();
|
||||
manager = GContext.container.Resolve<Manager>();
|
||||
}
|
||||
var loadRes = manager.Load();
|
||||
if (!loadRes || manager.EventId != e.ID)
|
||||
{
|
||||
manager.Init(e.ID);
|
||||
manager.Save();
|
||||
}
|
||||
UITypes.OfferStoryChainPanel.SetType(manager.GetUiAddressableUrl());
|
||||
}
|
||||
|
||||
public void Init(int eventId)
|
||||
{
|
||||
_eventId = eventId;
|
||||
_progress = 0;
|
||||
_redirectId = _tables.TbFishingEvent[_eventId].RedirectID;
|
||||
_vipOnTrigger = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
_roundCount = 0;
|
||||
}
|
||||
|
||||
public void InitTest()
|
||||
{
|
||||
_eventId = 114514;
|
||||
_progress = 0;
|
||||
_redirectId = 92101001;
|
||||
_vipOnTrigger = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
_roundCount = 0;
|
||||
}
|
||||
|
||||
public bool Load()
|
||||
{
|
||||
var s = saveStore.Read(PlayFabKey).ValueOrThrow();
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
Debug.LogWarning("[OfferStoryChain] No pf data found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var parts = s.Split(',');
|
||||
if (parts.Length != 4)
|
||||
{
|
||||
Debug.LogWarning($"[OfferStoryChain] String format error. Expect 4, but got {parts.Length}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!int.TryParse(parts[0], out int eventId)
|
||||
|| !int.TryParse(parts[1], out int progress)
|
||||
|| !int.TryParse(parts[2], out int vipLevel)
|
||||
|| !int.TryParse(parts[3], out int roundCount))
|
||||
{
|
||||
Debug.LogWarning($"[OfferStoryChain] Parsing error.");
|
||||
return false;
|
||||
}
|
||||
|
||||
_eventId = eventId;
|
||||
_progress = progress;
|
||||
_vipOnTrigger = vipLevel;
|
||||
_redirectId = _tables.TbFishingEvent[_eventId].RedirectID;
|
||||
_roundCount = roundCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("{0},{1},{2},{3}", _eventId, _progress, _vipOnTrigger, _roundCount);
|
||||
saveStore.Write(PlayFabKey, sb.ToString()).RequireAccepted();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_progress = 0;
|
||||
_roundCount = 0;
|
||||
Save();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Table Content
|
||||
private readonly Tables _tables = GContext.container.Resolve<Tables>();
|
||||
private int _redirectId;
|
||||
private EventOfferStoryChain TableMain => _tables.TbEventOfferStoryChain[_redirectId];
|
||||
private TbEventPackManager TablePacks => _tables.TbEventPackManager;
|
||||
|
||||
private List<int> GetPackIdList()
|
||||
{
|
||||
var vip = Mathf.Clamp(_vipOnTrigger, 0, TableMain.VipOfferList.Count - 1);
|
||||
return TableMain.VipOfferList[vip];
|
||||
}
|
||||
|
||||
private List<Pack> GetPackList(int packId)
|
||||
{
|
||||
return TablePacks[packId].VIPPackList[0].Select(p => _tables.TbPack[p]).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Max progress in one round.
|
||||
/// Progress more than this will either trigger next round or end this game.
|
||||
/// -1 because the last pack is the final reward.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int GetMaxProgressInRound()
|
||||
{
|
||||
return GetPackList(GetPackIdList()[0]).Count - 1;
|
||||
}
|
||||
|
||||
private int GetMaxRoundCount()
|
||||
{
|
||||
var vip = Mathf.Clamp(_vipOnTrigger, 0, TableMain.VipOfferList.Count - 1);
|
||||
return TableMain.VipOfferList[vip].Count;
|
||||
}
|
||||
|
||||
public int GetFinalRewardDropId(int roundCount)
|
||||
{
|
||||
roundCount = Mathf.Clamp(roundCount, 0, GetMaxRoundCount() - 1);
|
||||
var packIdList = GetPackIdList();
|
||||
var packList = GetPackList(packIdList[roundCount]);
|
||||
return packList[^1].DropID;
|
||||
}
|
||||
|
||||
public ScrollerItemViewData[] GetScrollerSlotDataArray(int roundCount)
|
||||
{
|
||||
var packIdList = GetPackIdList();
|
||||
var packList = GetPackList(packIdList[roundCount]);
|
||||
return Enumerable.Range(0, GetMaxProgressInRound())
|
||||
.Select(i => new ScrollerItemViewData
|
||||
{
|
||||
Index = i,
|
||||
EventId = _eventId,
|
||||
DropId = packList[i].DropID,
|
||||
IapId = packList[i].IAPID,
|
||||
State = GetStateFromIndex(i)
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region System
|
||||
|
||||
private OfferStoryChain.ScrollerItemViewState GetStateFromIndex(int idx)
|
||||
{
|
||||
if (idx < _progress)
|
||||
return OfferStoryChain.ScrollerItemViewState.Bought;
|
||||
if (idx == _progress)
|
||||
return OfferStoryChain.ScrollerItemViewState.Available;
|
||||
return OfferStoryChain.ScrollerItemViewState.Locked;
|
||||
|
||||
}
|
||||
public IEventAggregator EventAggregator = new EventAggregator();
|
||||
|
||||
public MainPanelData GetMainPanelData()
|
||||
{
|
||||
var res = new MainPanelData();
|
||||
res.Progress = _progress;
|
||||
res.Reward = _playerItemData.GetItemDataByDropIdLureInflation(null, GetFinalRewardDropId(_roundCount))[0];
|
||||
res.ScrollerItemViewDataList = GetScrollerSlotDataArray(_roundCount);
|
||||
res.SpineBaseName = TableMain.Spine;
|
||||
return res;
|
||||
}
|
||||
|
||||
public List<ItemData> GrantReward(int dropId)
|
||||
{
|
||||
var rewards = _playerItemData.AddItemByDropLureInflation(null, dropId, null);
|
||||
MoveToNextSlotAndTryGrantFinalReward();
|
||||
return rewards;
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<(bool res, List<ItemData> rewards)> GrantReward(int dropId, IAPItemList iapItemList, int eventId)
|
||||
{
|
||||
var rewards = _playerItemData.GetItemDataByDropIdLureInflation(null, dropId);
|
||||
var shopBuyTypeData = new ShopBuyTypeData { type = ShopBuyType.EventPack, ID = eventId };
|
||||
bool res = await GContext.container.Resolve<PlayerShopData>()
|
||||
.OnBuy(dropId, shopBuyTypeData, iapItemList, rewards);
|
||||
return (res, rewards);
|
||||
}
|
||||
|
||||
public void MoveToNextSlotAndTryGrantFinalReward()
|
||||
{
|
||||
Debug.Log($"[OfferStoryChain] Move to next slot. Current Progress: {_progress}");
|
||||
_progress++;
|
||||
if (_progress >= GetMaxProgressInRound())
|
||||
{
|
||||
var finalRewardDropId = GetFinalRewardDropId(_roundCount);
|
||||
_playerItemData.AddItemByDropLureInflation(null, finalRewardDropId, null, false);
|
||||
_progress = 0;
|
||||
_roundCount++;
|
||||
}
|
||||
Save();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Data
|
||||
public TimerContext GetTimerData()
|
||||
{
|
||||
var res = new TimerContext();
|
||||
(res.StartTime, res.ExpiryTime) = FtUtils.GetEventTimeLimit(_eventId);
|
||||
return res;
|
||||
}
|
||||
|
||||
public string GetEntrancIconUrl()
|
||||
{
|
||||
return TableMain.Icon;
|
||||
}
|
||||
|
||||
public string GetUiAddressableUrl()
|
||||
{
|
||||
return TableMain.Addressable;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class ScrollerItemViewData
|
||||
{
|
||||
// public ItemData[] ItemDataList { get; set; }
|
||||
// public string TextPrice { get; set; }
|
||||
public int Index { get; set; }
|
||||
public int EventId { get; set; }
|
||||
public int DropId { get; set; }
|
||||
public int IapId { get; set; }
|
||||
// public string TextDiscount { get; set; }
|
||||
public ScrollerItemViewState State { get; set; }
|
||||
}
|
||||
|
||||
public enum ScrollerItemViewState
|
||||
{
|
||||
Bought,
|
||||
Locked,
|
||||
Available
|
||||
}
|
||||
|
||||
public class MainPanelData
|
||||
{
|
||||
public int Progress { get; set; }
|
||||
public ItemData Reward { get; set; }
|
||||
public ScrollerItemViewData[] ScrollerItemViewDataList { get; set; }
|
||||
public string SpineBaseName { get; set; }
|
||||
// public System.DateTime StartTime { get; set; }
|
||||
// public System.DateTime EndTime { get; set; }
|
||||
}
|
||||
|
||||
public class OfferStoryChainCloseEvent { }
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
using System;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using game;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OrderManager;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Scripts.UI.OfferSweep
|
||||
{
|
||||
public class OfferSweepManager : IDisposable,IOrderReplenishment
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public OfferSweepManager()
|
||||
{
|
||||
GContext.OnEvent<TargetEvent>().Subscribe(OnEvent_TargetEvent).AddTo(Disposables);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposables?.Dispose();
|
||||
}
|
||||
|
||||
private CompositeDisposable _disposable;
|
||||
private CompositeDisposable Disposables => _disposable ??= new CompositeDisposable();
|
||||
#region event
|
||||
public void OnEvent_TargetEvent(TargetEvent targetEvent)
|
||||
{
|
||||
if (targetEvent.type == 4)
|
||||
{
|
||||
RefreshData();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region data
|
||||
[Inject]
|
||||
public Tables Tables { get; set; }
|
||||
private OfferSweepPackData _sweepPackData;
|
||||
public OfferSweepPackData SweepPackData => _sweepPackData ??= new OfferSweepPackData();
|
||||
|
||||
private OfferSweepPackData GetCachedData()
|
||||
{
|
||||
var dataStr = saveStore.Read(nameof(OfferSweepPackData)).ValueOrThrow();
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd OfferSweepPackData LoadData sData {dataStr}");
|
||||
#endif
|
||||
return dataStr != null ? (JsonConvert.DeserializeObject<OfferSweepPackData>(dataStr) ?? throw new JsonSerializationException("Null sweep save.")) : new OfferSweepPackData();
|
||||
}
|
||||
|
||||
public void RefreshData()
|
||||
{
|
||||
var isResetData = IsResetData(SweepPackData, out int eventId, out int sweepPackId);
|
||||
if (isResetData)
|
||||
{
|
||||
SweepPackData.FishingEventID = eventId;
|
||||
SweepPackData.SweepPackId = sweepPackId;
|
||||
SweepPackData.StartTimeVipLevel = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
SweepPackData.PurchasePackDir?.Clear();
|
||||
SweepPackData.PurchasedCount = 0;
|
||||
SaveData();
|
||||
}
|
||||
else
|
||||
{
|
||||
var cachedData = GetCachedData();
|
||||
SweepPackData.SetData(cachedData);
|
||||
}
|
||||
|
||||
|
||||
if (!SweepPackData.IsActive) return;
|
||||
var packManager = GetOfferSweepEventPackManager();
|
||||
UITypes.OfferSweepPanel.SetType(packManager.Panel);
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(eventId, UITypes.OfferSweepPanel, 0);
|
||||
}
|
||||
private bool IsResetData(OfferSweepPackData offerSweepPackData, out int eventId, out int sweepPackId)
|
||||
{
|
||||
eventId = sweepPackId = 0;
|
||||
|
||||
if (offerSweepPackData == null) return true;
|
||||
|
||||
var @event = GetCurrentLoopEvent();
|
||||
if (@event == null) return true;
|
||||
eventId = @event.ID;
|
||||
|
||||
sweepPackId = GetOfferSweepPackId(@event);
|
||||
if (sweepPackId <= 0) return true;
|
||||
|
||||
var cacheData = GetCachedData();
|
||||
if (cacheData == null) return false;
|
||||
if (cacheData.FishingEventID != @event.ID) return true;
|
||||
if (cacheData.SweepPackId == 0) cacheData.SweepPackId = sweepPackId;
|
||||
|
||||
return false;
|
||||
}
|
||||
public void SaveData()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd OfferSweepPackData SaveData {JsonConvert.SerializeObject(SweepPackData)}");
|
||||
#endif
|
||||
saveStore.Write(nameof(OfferSweepPackData), JsonConvert.SerializeObject(SweepPackData)).RequireAccepted();
|
||||
}
|
||||
|
||||
public FishingEvent GetCurrentLoopEvent()
|
||||
{
|
||||
//4 循环活动
|
||||
var events = Tables.TbFishingEvent.GetAllDataByType(4);
|
||||
var fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
foreach (var e in events)
|
||||
{
|
||||
if (!EventStrategyFactory.IsRegisterEventBySubType(e.SubType))
|
||||
continue;
|
||||
var isOpen = fishingEventData.Condition(e);
|
||||
if (isOpen) return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public EventPackManager GetOfferSweepEventPackManager(FishingEvent fishingEvent = null)
|
||||
{
|
||||
return Tables.TbEventPackManager.GetOrDefault(SweepPackData.SweepPackId);
|
||||
}
|
||||
public bool GetData(int index, out EventPackManager packManager, out Pack packConfig, out List<ItemData> itemDatas, out IAPItemList iapItem, out int packId)
|
||||
{
|
||||
packManager = null;
|
||||
packConfig = null;
|
||||
itemDatas = null;
|
||||
iapItem = null;
|
||||
packId = 0;
|
||||
|
||||
packManager = GetOfferSweepEventPackManager();
|
||||
if (packManager == null) return false;
|
||||
var VipLevel = this.SweepPackData.StartTimeVipLevel;
|
||||
if (packManager.VIPPackList == null || packManager.VIPPackList[VipLevel].Count != 7)
|
||||
{
|
||||
Debug.LogError($"配置错误 eventPackManager ID {packManager.ID} VIP {VipLevel} list count {packManager.VIPPackList[VipLevel]?.Count}");
|
||||
return false;
|
||||
}
|
||||
|
||||
packId = packManager.VIPPackList?[VipLevel]?[index] ?? 0;
|
||||
|
||||
packConfig = Tables.TbPack?.GetOrDefault(packId);
|
||||
if (packConfig == null)
|
||||
{
|
||||
Debug.LogError($"配置错误 eventPackManager id {packId}");
|
||||
return false;
|
||||
}
|
||||
|
||||
itemDatas = GContext.container.Resolve<PlayerItemData>().GetItemDataByDropIdLureInflation(null, packConfig.DropID);
|
||||
if (itemDatas == null) return false;
|
||||
|
||||
iapItem = Tables.TbIAPItemList?.GetOrDefault(packConfig.IAPID);
|
||||
if (iapItem == null) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<ItemData> GetPurchasedItemDataByIndex(int index)
|
||||
{
|
||||
if (SweepPackData == null) return null;
|
||||
if (index < 0) return null;
|
||||
if (!SweepPackData.PurchasePackDir.ContainsKey(index)) return null;
|
||||
var packId = SweepPackData.PurchasePackDir[index];
|
||||
var packConfig = Tables.TbPack?.GetOrDefault(packId);
|
||||
if (packConfig == null)
|
||||
{
|
||||
Debug.LogError($"配置错误 eventPackManager id {packId}");
|
||||
return null;
|
||||
}
|
||||
return GContext.container.Resolve<PlayerItemData>().GetItemDataByDropId(packConfig.DropID);
|
||||
}
|
||||
#endregion
|
||||
#region static method
|
||||
public static int GetOfferSweepPackId(FishingEvent fishingEvent = null)
|
||||
{
|
||||
if (fishingEvent == null)
|
||||
fishingEvent = GContext.container.Resolve<OfferSweepManager>().GetCurrentLoopEvent();
|
||||
|
||||
if (fishingEvent == null) return 0;
|
||||
|
||||
var strategy = EventStrategyFactory.GetStrategy(fishingEvent.SubType);
|
||||
var tables = GContext.container.Resolve<Tables>();
|
||||
var cycleConfig = tables.TbFishingEventCycleItem.GetOrDefault(fishingEvent.RedirectID);
|
||||
return strategy.GetSweepPackId(cycleConfig.RedirectID);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void OnReplenish(ShopBuyTypeData buyType, IAPItemList iAPItemList, List<ItemData> itemDataGift, RewardType rewardShowType,
|
||||
bool voucher = false)
|
||||
{
|
||||
if(buyType.extraPurcheData==null) return;
|
||||
if (!buyType.extraPurcheData.TryGetValue(nameof(OfferSweepPackData), out var value)) return;
|
||||
if (!buyType.extraPurcheData.TryGetValue(nameof(_sweepPackData.FishingEventID), out var fishingEventIdjToken))
|
||||
{
|
||||
Debug.LogError($"参数错误 eventPackManager id {_sweepPackData.FishingEventID}");
|
||||
return;
|
||||
}
|
||||
var eventID= fishingEventIdjToken.ToObject<int>();
|
||||
if (eventID != _sweepPackData.FishingEventID)
|
||||
{
|
||||
//todo 保底机制
|
||||
return;
|
||||
}
|
||||
|
||||
var packIDs = value.ToObject<Dictionary<int, int>>();
|
||||
if(packIDs==null) return;
|
||||
foreach (var packID in packIDs)
|
||||
{
|
||||
_sweepPackData.PurchasePackDir.TryAdd(packID.Key, packID.Value);
|
||||
}
|
||||
if (_sweepPackData.PurchasePackDir.Count == 6)
|
||||
{
|
||||
_sweepPackData.PurchasedCount++;
|
||||
}
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
using System.Collections.Generic;
|
||||
using GameCore;
|
||||
using System.Linq;
|
||||
using asap.core;
|
||||
using UnityEngine;
|
||||
using cfg;
|
||||
using System.Text;
|
||||
using game;
|
||||
|
||||
namespace OfferTreasureAscent
|
||||
{
|
||||
public class Manager
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
private int _status = 0, _eventId, _redirectId;
|
||||
private List<int> _failedPurchaseRewardIndices;
|
||||
private PlayerItemData _playerItemData => GContext.container.Resolve<PlayerItemData>();
|
||||
private cfg.Tables _tables => GContext.container.Resolve<cfg.Tables>();
|
||||
public int EventId => _eventId;
|
||||
public int RoundCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return FtUtils.PopCount(_status);
|
||||
}
|
||||
}
|
||||
|
||||
public int Status => _status;
|
||||
#region RedPoint
|
||||
public bool DoNeedRedPoint()
|
||||
{
|
||||
return GetIapId() <= 0;
|
||||
}
|
||||
|
||||
public readonly string RedPointKey = "offer_treasure_ascent";
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataStorage
|
||||
|
||||
public const string PfKey = "OfferTreasureAscent";
|
||||
public static void InitEvent(FishingEvent e)
|
||||
{
|
||||
var manager = GContext.container.Resolve<Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
GContext.container.Register<Manager>().AsSingleton();
|
||||
manager = GContext.container.Resolve<Manager>();
|
||||
}
|
||||
var loadRes = manager.Load();
|
||||
if (!loadRes || e.ID != manager.EventId)
|
||||
{
|
||||
manager.Init(e.ID);
|
||||
manager.Save();
|
||||
}
|
||||
UITypes.OfferTreasureAscentMainPanel.SetType(manager.GetUiData()[0]);
|
||||
UITypes.OfferTreasureAscentBoxPanel.SetType(manager.GetUiData()[2]);
|
||||
}
|
||||
|
||||
public void InitTest()
|
||||
{
|
||||
_eventId = 90400139;
|
||||
_redirectId = 1001;
|
||||
_status = 0;
|
||||
_failedPurchaseRewardIndices = new List<int>();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_status = 0;
|
||||
_failedPurchaseRewardIndices = new List<int>();
|
||||
Save();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("{0},{1},", _eventId, _status);
|
||||
if (_failedPurchaseRewardIndices != null && _failedPurchaseRewardIndices.Count > 0)
|
||||
foreach (var i in _failedPurchaseRewardIndices)
|
||||
sb.AppendFormat("{0},", i);
|
||||
saveStore.Write(PfKey, sb.ToString()).RequireAccepted();
|
||||
}
|
||||
|
||||
public bool Load()
|
||||
{
|
||||
try
|
||||
{
|
||||
var s = saveStore.Read(PfKey).ValueOrThrow();
|
||||
if (s == null || s == "")
|
||||
return false;
|
||||
var tokens = s.Trim(',').Split(',');
|
||||
if (tokens.Length < 2)
|
||||
return false;
|
||||
_eventId = int.Parse(tokens[0]);
|
||||
_status = int.Parse(tokens[1]);
|
||||
_failedPurchaseRewardIndices = new List<int>();
|
||||
for (int i = 2; i < tokens.Length; i++)
|
||||
_failedPurchaseRewardIndices.Add(int.Parse(tokens[i]));
|
||||
_redirectId = _tables.TbFishingEvent[_eventId].RedirectID;
|
||||
return true;
|
||||
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogWarning(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Init(int eventId)
|
||||
{
|
||||
_eventId = eventId;
|
||||
_redirectId = _tables.TbFishingEvent[_eventId].RedirectID;
|
||||
_status = 0;
|
||||
_failedPurchaseRewardIndices = new List<int>();
|
||||
Save();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TableContext
|
||||
|
||||
private EventOfferTreasureAscent TbMain
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tables.TbEventOfferTreasureAscent[_redirectId];
|
||||
}
|
||||
}
|
||||
|
||||
public List<EventOfferTreasureAscentRewards> GetRandomizedRewardList()
|
||||
{
|
||||
// warp the ordered list to randomize the order, with player playfab id as seed so that player got consistent result.
|
||||
var id = GContext.container.Resolve<IUserService>().UserId;
|
||||
// treat playfab id as an ordinary string instead of a hexadicimal number, and roll it into a hash. Consistant for each player.
|
||||
int seed = id.Aggregate(17, (hash,c)=> hash * 31 + c);
|
||||
var rng = new System.Random(seed);
|
||||
// var rng = new System.Random((int)System.Convert.ToUInt64(id, 16));
|
||||
|
||||
var res = _tables.TbEventOfferTreasureAscentRewards.DataList.ToList();
|
||||
|
||||
FtUtils.ShuffleList(res, rng);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get item data from reward index. Will return null if no table data found. Note that the ID of the item, which is not used here, is not the same as the index.
|
||||
/// </summary>
|
||||
/// <param name="idx">Index of rewards.</param>
|
||||
/// <returns>The ItemData, after transition and stuff. Will return null if not found.</returns>
|
||||
public ItemData GetItemFromIndex(int idx)
|
||||
{
|
||||
// var tbRewards = _tables.TbEventOfferTreasureAscentRewards.DataList;
|
||||
var tbRewards = GetRandomizedRewardList();
|
||||
idx = Mathf.Clamp(idx, 0, tbRewards.Count - 1);
|
||||
var reward = tbRewards[idx];
|
||||
var res = new ItemData(reward.Item, reward.Number);
|
||||
_playerItemData.ItemTransition(res);
|
||||
_playerItemData.LureInflation(res, null);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pick random reward index.
|
||||
/// </summary>
|
||||
/// <param name="currentRound">Current round count that limits the option.</param>
|
||||
/// <returns>Reward idx. -1 if not found.</returns>
|
||||
public int PickRandomAvailableRewardIdx(int currentRound)
|
||||
{
|
||||
// var tbRewards = _tables.TbEventOfferTreasureAscentRewards.DataList;
|
||||
var tbRewards = GetRandomizedRewardList();
|
||||
var availableIdxList = tbRewards
|
||||
.Select((r, i) => (r, i))
|
||||
.Where(ri => ri.r.LimitConfig - 1 <= currentRound && !IsClaimed(ri.i))
|
||||
.Select(ri => ri.i)
|
||||
.ToList();
|
||||
if (availableIdxList.Count <= 0)
|
||||
{
|
||||
Debug.LogError($"[OfferTreasureAscent] No available reward found in round {currentRound}. This is not supposed to happen.");
|
||||
return -1;
|
||||
}
|
||||
var weightList = availableIdxList.Select(i => tbRewards[i].Weight).ToList();
|
||||
return FtUtils.PickRandomItemWithWeight(availableIdxList, i => weightList[i]);
|
||||
}
|
||||
|
||||
public int PickRandomAvailableRewardIdx()
|
||||
{
|
||||
return PickRandomAvailableRewardIdx(RoundCount);
|
||||
}
|
||||
|
||||
public int GetRewardCount()
|
||||
{
|
||||
return _tables.TbEventOfferTreasureAscentRewards.DataList.Count;
|
||||
}
|
||||
|
||||
public async System.Threading.Tasks.Task<(int normalRewardIdx, List<ItemData> finalRewards, int iap_id)> GrantRandomReward()
|
||||
{
|
||||
var iapId = GetIapId();
|
||||
var pickedRewardIdx = PickRandomAvailableRewardIdx();
|
||||
List<ItemData> finalRewards = null;
|
||||
if (iapId > 0)
|
||||
{
|
||||
GContext.container.Resolve<PlayerItemData>().ResolveIapId(iapId, out var iapItemList, out _);
|
||||
var buyTypeData = new ShopBuyTypeData
|
||||
{
|
||||
type = ShopBuyType.EventPack,
|
||||
ID = EventId,
|
||||
NoShow = true
|
||||
};
|
||||
var rewardItem = GetItemFromIndex(pickedRewardIdx);
|
||||
if (pickedRewardIdx < 0 || rewardItem == null)
|
||||
{
|
||||
Debug.LogError("[OfferTreasureAscent] No reward to claim.");
|
||||
return (-1, null, iapId);
|
||||
}
|
||||
// Drop id required here is fill with 0, because it is a legacy feature and is no longer used.
|
||||
var res = await GContext.container.Resolve<PlayerShopData>()
|
||||
.OnBuy(0, buyTypeData, iapItemList, new List<ItemData>() { rewardItem });
|
||||
if (res)
|
||||
{
|
||||
Claim(pickedRewardIdx);
|
||||
// Debug.Log($"[OfferTreasureAscent] Grant paid {pickedRewardIdx}, ID {pickedRewardIdx + 1}");
|
||||
finalRewards = TryGrantFinalReward();
|
||||
}
|
||||
else
|
||||
{
|
||||
RecordFailedRewardIdx(pickedRewardIdx);
|
||||
return (-1, null, iapId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pickedRewardIdx >= 0)
|
||||
{
|
||||
Claim(pickedRewardIdx);
|
||||
TryGrantReward(pickedRewardIdx);
|
||||
finalRewards = TryGrantFinalReward();
|
||||
}
|
||||
}
|
||||
return (pickedRewardIdx, finalRewards, iapId);
|
||||
}
|
||||
|
||||
public List<ItemData> GetRewards()
|
||||
{
|
||||
var res = new List<ItemData>();
|
||||
for (int i = 0; i < GetRewardCount(); i++)
|
||||
res.Add(GetItemFromIndex(i));
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get IAP ID list. Fill in 0s for free chances.
|
||||
/// </summary>
|
||||
/// <returns>IAP ID</returns>
|
||||
public List<int> GetIapIdList()
|
||||
{
|
||||
var res = new List<int>();
|
||||
for (int i = 0; i < TbMain.FreeChance; i++)
|
||||
{
|
||||
res.Add(0);
|
||||
}
|
||||
res.AddRange(TbMain.IapList);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get IAP ID. Get 0 for free chance.
|
||||
/// </summary>
|
||||
/// <param name="currentRound">Round count. Starts with 0.</param>
|
||||
/// <returns>IAP ID, get 0 for free chance.</returns>
|
||||
public int GetIapId(int currentRound)
|
||||
{
|
||||
var iapIdList = GetIapIdList();
|
||||
currentRound = Mathf.Clamp(currentRound, 0, iapIdList.Count - 1);
|
||||
return iapIdList[currentRound];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get IAP ID. Get 0 for free chance. For current round.
|
||||
/// </summary>
|
||||
/// <returns>IAP ID of this round. 0 for free chance.</returns>
|
||||
public int GetIapId()
|
||||
{
|
||||
return GetIapId(RoundCount);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI data
|
||||
/// <summary>
|
||||
/// Get UI Data.
|
||||
/// </summary>
|
||||
/// <returns>First is main panel, second is the grand reward showing animation panel.</returns>
|
||||
public string[] GetUiData()
|
||||
{
|
||||
return new string[] { TbMain.UIPanel, TbMain.Icon, TbMain.BoxOpenPrefab};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Entrance button data.
|
||||
/// </summary>
|
||||
/// <param name="idx"></param>
|
||||
public OfferTreasureAscentEntranceData GetEntranceData()
|
||||
{
|
||||
var res = new OfferTreasureAscentEntranceData();
|
||||
res.IconUrl = TbMain.Icon;
|
||||
(var startTime, var endTime) = FtUtils.GetEventTimeLimit(EventId);
|
||||
res.TimerData = new TimerContext
|
||||
{
|
||||
StartTime = startTime,
|
||||
ExpiryTime = endTime
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
public GeneralProbabilityData[] GetProbabilityData()
|
||||
{
|
||||
var res = new GeneralProbabilityData[GetRewardCount()];
|
||||
var tbRewards = GetRandomizedRewardList();
|
||||
var totalWeight = tbRewards.Where((r, i) => !IsClaimed(i)).Sum(r => r.Weight);
|
||||
for (int i = 0; i < GetRewardCount(); i++)
|
||||
{
|
||||
res[i] = new GeneralProbabilityData
|
||||
{
|
||||
Item = GetItemFromIndex(i),
|
||||
Probability = tbRewards[i].Weight / (float)totalWeight,
|
||||
IsClaimed = IsClaimed(i)
|
||||
};
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region System
|
||||
|
||||
public void Claim(int idx)
|
||||
{
|
||||
_status |= 1 << idx;
|
||||
Save();
|
||||
}
|
||||
|
||||
public bool IsClaimed(int idx)
|
||||
{
|
||||
return (_status & (1 << idx)) != 0;
|
||||
}
|
||||
|
||||
public ItemData GetGrandReward()
|
||||
{
|
||||
return new ItemData(TbMain.ChestReward, 1);
|
||||
}
|
||||
|
||||
public int GetGrandRewardItemId()
|
||||
{
|
||||
return TbMain.ChestReward;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if all normal rewards are claimed
|
||||
/// </summary>
|
||||
/// <returns>True is all is claimed.</returns>
|
||||
public bool IsNormalRewardsDepleted
|
||||
{
|
||||
get
|
||||
{
|
||||
int rewardCount = GetRewardCount();
|
||||
|
||||
if (rewardCount <= 0)
|
||||
return true;
|
||||
|
||||
if (rewardCount > 31)
|
||||
{
|
||||
UnityEngine.Debug.LogError("[OfferTreasureAscent] Reward count is too large for int: " + rewardCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
int expectedMask = (1 << rewardCount) - 1;
|
||||
return (_status & expectedMask) == expectedMask;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFinalRewardClaimed
|
||||
{
|
||||
get
|
||||
{
|
||||
return IsNormalRewardsDepleted && IsClaimed(GetRewardCount());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grant reward according to reward index, and try grant final reward if all rewards are claimed.
|
||||
/// </summary>
|
||||
/// <param name="rewardIdx">The index of the target reward.</param>
|
||||
public void TryGrantReward(int rewardIdx)
|
||||
{
|
||||
Debug.Log($"[OfferTreasureAscent] Grant free {rewardIdx}, ID {rewardIdx + 1}");
|
||||
var rewardItem = GetItemFromIndex(rewardIdx);
|
||||
if (rewardItem == null)
|
||||
{
|
||||
Debug.LogError("[OfferTreasureAscent] Reward item is null. This is not supposed to happen.");
|
||||
return;
|
||||
}
|
||||
_playerItemData.AddItem(rewardItem, null);
|
||||
GContext.Publish(new ShowData(rewardItem));
|
||||
}
|
||||
|
||||
public List<ItemData> TryGrantFinalReward()
|
||||
{
|
||||
// Debug.Log("[OfferTreasureAscent]Try Grant Final...");
|
||||
if (!IsNormalRewardsDepleted || IsFinalRewardClaimed)
|
||||
return null;
|
||||
// var grandReward = GetGrandReward();
|
||||
// _playerItemData.AddItem(grandReward, null);
|
||||
var grandRewardId = GetGrandRewardItemId();
|
||||
var items = FtUtils.ResolveChestContent(grandRewardId);
|
||||
if (items.Count <= 0)
|
||||
{
|
||||
Debug.LogError("[OfferTreasureAscent] Grand reward is empty.");
|
||||
return null;
|
||||
}
|
||||
_playerItemData.AddItem(items, null);
|
||||
Claim(GetRewardCount());
|
||||
// Debug.Log("[OfferTreasureAscent] Grant Final.");
|
||||
return items;
|
||||
}
|
||||
|
||||
public void OnBuySuccess()
|
||||
{
|
||||
if (_failedPurchaseRewardIndices is { Count: > 0 })
|
||||
{
|
||||
foreach (var idx in _failedPurchaseRewardIndices)
|
||||
Claim(idx);
|
||||
_failedPurchaseRewardIndices.Clear();
|
||||
Save();
|
||||
}
|
||||
TryGrantFinalReward();
|
||||
}
|
||||
|
||||
public void RecordFailedRewardIdx(int idx)
|
||||
{
|
||||
_failedPurchaseRewardIndices.Add(idx);
|
||||
Save();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,179 @@
|
||||
using asap.core;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UniRx;
|
||||
using GameCore;
|
||||
using game;
|
||||
using static EventPartnerData;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class EventPartnerEntryButton : EventButtonResource
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
[SerializeField] private TMP_Text _textTimer;
|
||||
[SerializeField] private Button _btn;
|
||||
[SerializeField] CanvasGroup _homeCanvasGroup;
|
||||
[SerializeField] private Image icon;
|
||||
private readonly IUserService _userService = GContext.container.Resolve<IUserService>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
// Debug.Log($"<color=#e23f31>[EventPartner] Icon Awake</color>");
|
||||
GContext.OnEvent<EventPartnerDataLoaded>().Subscribe(_ => OnDataLoaded()).AddTo(this);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
var _data = GContext.container.Resolve<EventPartnerData>();
|
||||
// _data.LogActivation();
|
||||
|
||||
//var manager = GContext.container.Resolve(Partainer)?
|
||||
|
||||
if (!_data.IsEventActivated)
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
return;
|
||||
}
|
||||
_textTimer.text = ConvertTools.ConvertTime2(_data.RemainingTime);
|
||||
RedPointManager.Instance.SetRedPointState("Home.Fishbowl", _data.DoNeedRedPoint);
|
||||
Observable.Interval(System.TimeSpan.FromSeconds(1.0f)).Subscribe(_ =>
|
||||
{
|
||||
_textTimer.text = ConvertTools.ConvertTime2(_data.RemainingTime);
|
||||
RedPointManager.Instance.SetRedPointState("Home.Fishbowl", _data.DoNeedRedPoint);
|
||||
if (_data.RemainingTime.TotalSeconds <= 0) gameObject.SetActive(false);
|
||||
}).AddTo(this);
|
||||
// Debug.Log($"<color=#e23f31>icon: {_data.Icon}</color>");
|
||||
CheckResource(new List<string>() { "EventPartnerCommonPrefabs", _data.AddressableLabel, _data.Icon });
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_btn.onClick.AddListener(() => EnterEventPartnerAct());
|
||||
}
|
||||
|
||||
private async void EnterEventPartnerAct()
|
||||
{
|
||||
try
|
||||
{
|
||||
var _data = GContext.container.Resolve<EventPartnerData>();
|
||||
_homeCanvasGroup.blocksRaycasts = false;
|
||||
var isWithinCoolDown = _data.LastDetailRequestFromEntranceButton != null
|
||||
&& ZZTimeHelper.UtcNow() - _data.LastDetailRequestFromEntranceButton <= System.TimeSpan.FromSeconds(15);
|
||||
|
||||
if (!_data.IsWhole || !isWithinCoolDown)
|
||||
{
|
||||
_data.LastDetailRequestFromEntranceButton = ZZTimeHelper.UtcNow();
|
||||
var dataRes = await LoadData();
|
||||
if (!dataRes)
|
||||
{
|
||||
Debug.Log($"<color=red>[EventPartner] LoadData Failed.</color>");
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_107"));
|
||||
_homeCanvasGroup.blocksRaycasts = true;
|
||||
_data.IsLastTimeDetailRequestFromEntranceButtonValid = false;
|
||||
return;
|
||||
}
|
||||
_data.IsLastTimeDetailRequestFromEntranceButtonValid = true;
|
||||
// else open panel.
|
||||
}
|
||||
else if (!_data.IsLastTimeDetailRequestFromEntranceButtonValid)
|
||||
{
|
||||
Debug.Log($"<color=red>[EventPartner] Too frequent.</color>");
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_107"));
|
||||
_homeCanvasGroup.blocksRaycasts = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Load resource and enter event act
|
||||
_data.SubscribeEvent();
|
||||
if (await LoadAssetBundle())
|
||||
GContext.Publish(new UnloadActToNextAct("EventPartnerAct"));
|
||||
else
|
||||
{
|
||||
var panel = await UIManager.Instance.ShowUI(UITypes.CloudTransitionPanel);
|
||||
panel.GetComponent<CloudTransitionPanel>().SetBtn(true, () => GContext.Publish(new UnloadActToNextAct("EventPartnerAct")));
|
||||
}
|
||||
_homeCanvasGroup.blocksRaycasts = true;
|
||||
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.Log($"<color=#22a6f2>[EventPartner] EnterActError: {e.Message}\n{e.StackTrace}</color>");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task<bool> LoadData()
|
||||
{
|
||||
var _data = GContext.container.Resolve<EventPartnerData>();
|
||||
var _playerPreferenceKey = "EventPartnerRecommendCache" + _userService.UserId;
|
||||
//Load Player Preferences
|
||||
var pfString = saveStore.Read(EventPartnerPlayfabData.Key).ValueOrThrow();
|
||||
EventPartnerPlayfabData pfData = EventPartnerPlayfabData.FromString(pfString);
|
||||
Debug.Log($"<color=#2d7cee>[EventPartner] Try get response in load data from entrance:</color>");
|
||||
var detailRequest = new EventBuildDetailRequest() { eventId = _data.EventId };
|
||||
var response = await GContext.container.Resolve<ICustomServerMgr>()
|
||||
.EventPartnerRequest<EventBuildDetailResp>(DetailUrl, detailRequest);
|
||||
if (this == null || !saveStore.IsCurrent) return false;
|
||||
if (response == null)
|
||||
{
|
||||
Debug.Log($"<color=red>[EventPartner] No response. </color>");
|
||||
return false;
|
||||
}
|
||||
Debug.Log($"<color=#2d7cee>[EventPartner] Response: {response}</color>");
|
||||
Debug.Log($"<color=#2d7cee>[EventPartner] ResponseState: {response.State}</color>");
|
||||
Debug.Log($"<color=#2d7cee>[EventPartner] ResponseMessage: {response.Message}</color>");
|
||||
switch (response.State)
|
||||
{
|
||||
case EEventBuildState.Failed:
|
||||
Debug.Log($"<color=red>[EventPartner] Load data error from server: {response.Message} </color>");
|
||||
return false;
|
||||
case EEventBuildState.NoActiveEvent:
|
||||
Debug.Log($"<color=red>[EventPartner] Event partner not active in server: {response.Message}, but this is not supposed to happen any more.</color>");
|
||||
return false;
|
||||
case EEventBuildState.Success:
|
||||
_data.ResolveAllDataButEvent(response, _data.Cache, pfData);
|
||||
return true;
|
||||
default:
|
||||
Debug.Log($"<color=red>[EventPartner] Unexpected status: {response.State}./nMessage: {response.Message} </color>");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task<bool> LoadAssetBundle()
|
||||
{
|
||||
var _data = GContext.container.Resolve<EventPartnerData>();
|
||||
var loadResourceService = GContext.container.Resolve<ILoadResourceService>();
|
||||
var isCommentAssetLoaded = await loadResourceService.Load("EventPartnerCommonPrefabs");
|
||||
var isCurrentAssetLoaded = await loadResourceService.Load(_data.AddressableLabel);
|
||||
return isCommentAssetLoaded && isCurrentAssetLoaded;
|
||||
}
|
||||
|
||||
private async void OnDataLoaded()
|
||||
{
|
||||
Debug.Log($"<color=#2d7cee>[EventPartner]On Loaded.</color>");
|
||||
await Awaiters.NextFrame;
|
||||
//gameObject.SetActive(true);
|
||||
var _data = GContext.container.Resolve<EventPartnerData>();
|
||||
CheckResource(new List<string>() { "EventPartnerCommonPrefabs", _data.AddressableLabel, _data.Icon });
|
||||
}
|
||||
|
||||
protected override void OnLoadEventResource()
|
||||
{
|
||||
var _data = GContext.container.Resolve<EventPartnerData>();
|
||||
if (_data == null)
|
||||
{
|
||||
Debug.LogError("EventPartnerEntryButton EventPartnerData 为空");
|
||||
return;
|
||||
}
|
||||
// _data.LogActivation();
|
||||
if (_data.IsEventActivated)
|
||||
{
|
||||
GContext.container.Resolve<IUIService>().SetImageSprite(icon, _data.Icon);
|
||||
gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
public class EventPartnerDataLoaded { }
|
||||
@@ -0,0 +1,112 @@
|
||||
using asap.core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
public class EventPartnerStatuePlayfabData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
public int EventId;
|
||||
public int TicketCount;
|
||||
public int BuffFreeMultiplier;
|
||||
public DateTime BuffPointExpiryTime;
|
||||
// public List<string> PartnerIdList;
|
||||
public Dictionary<string, (int self, int partner)> DisplayScoreMap;
|
||||
public Dictionary<string, (int self, int bot)> RobotScoreMap;
|
||||
public bool IsFinalRewardClaimed;
|
||||
public float LureInflationRate;
|
||||
public bool IsEnableAutoMatch;
|
||||
/// <summary>UTC 时间,开启自动匹配时记录;用于关闭时的 CD 与 Gather 活动一致。</summary>
|
||||
public DateTime LastAutoMatchTimeUtc = DateTime.MinValue;
|
||||
private const char Pipe = '|', Comma = ',';
|
||||
public const string Key = "EventPartnerStatueData";
|
||||
|
||||
public string Serialized()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(EventId).Append(Pipe);
|
||||
sb.Append(TicketCount).Append(Pipe);
|
||||
sb.Append(BuffFreeMultiplier).Append(Pipe);
|
||||
sb.Append(BuffPointExpiryTime.ToString()).Append(Pipe);
|
||||
sb.Append(IsFinalRewardClaimed.ToString()).Append(Pipe);
|
||||
sb.Append(LureInflationRate).Append(Pipe);
|
||||
foreach (var kv in DisplayScoreMap)
|
||||
sb.Append(kv.Key).Append(Comma).Append(kv.Value.self).Append(Comma).Append(kv.Value.partner).Append(Comma);
|
||||
sb.Append(Pipe);
|
||||
foreach (var kv in RobotScoreMap)
|
||||
sb.Append(kv.Key).Append(Comma).Append(kv.Value.self).Append(Comma).Append(kv.Value.bot).Append(Comma);
|
||||
sb.Append(Pipe);
|
||||
sb.Append(IsEnableAutoMatch.ToString()).Append(Pipe);
|
||||
sb.Append(LastAutoMatchTimeUtc.ToString("o"));
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will return null if deserialization failed
|
||||
/// </summary>
|
||||
/// <param name="s">String from playfab</param>
|
||||
/// <returns>Playfab Data. Null if fails.</returns>
|
||||
public static EventPartnerStatuePlayfabData Deserialize(string s)
|
||||
{
|
||||
try
|
||||
{
|
||||
var arr = s.Trim(Pipe).Split(Pipe);
|
||||
var res = new EventPartnerStatuePlayfabData();
|
||||
res.EventId = int.Parse(arr[0]);
|
||||
res.TicketCount = int.Parse(arr[1]);
|
||||
res.BuffFreeMultiplier = int.Parse(arr[2]);
|
||||
res.BuffPointExpiryTime = DateTime.Parse(arr[3]);
|
||||
res.DisplayScoreMap = new Dictionary<string, (int self, int partner)>();
|
||||
res.RobotScoreMap = new Dictionary<string, (int self, int bot)>();
|
||||
res.IsFinalRewardClaimed = bool.Parse(arr[4]);
|
||||
res.LureInflationRate = float.Parse(arr[5]);
|
||||
if (arr.Length <= 6)
|
||||
return res;
|
||||
if (!string.IsNullOrEmpty(arr[6]))
|
||||
{
|
||||
var displayScoreStringList = arr[6].Trim(Comma).Split(Comma);
|
||||
for (int i = 0; i + 2 < displayScoreStringList.Length; i += 3)
|
||||
res.DisplayScoreMap.Add(displayScoreStringList[i], (int.Parse(displayScoreStringList[i + 1]), int.Parse(displayScoreStringList[i + 2])));
|
||||
}
|
||||
|
||||
if (arr.Length <= 7)
|
||||
return res;
|
||||
if (!string.IsNullOrEmpty(arr[7]))
|
||||
{
|
||||
var robotScoreStringList = arr[7].Trim(Comma).Split(Comma);
|
||||
for (int i = 0; i + 2 < robotScoreStringList.Length; i += 3)
|
||||
res.RobotScoreMap.Add(robotScoreStringList[i], (int.Parse(robotScoreStringList[i + 1]), int.Parse(robotScoreStringList[i + 2])));
|
||||
}
|
||||
|
||||
if (arr.Length > 8)
|
||||
res.IsEnableAutoMatch = bool.TryParse(arr[8], out var v) && v;
|
||||
|
||||
if (arr.Length > 9 && !string.IsNullOrEmpty(arr[9]) &&
|
||||
DateTime.TryParse(arr[9], null, System.Globalization.DateTimeStyles.RoundtripKind, out var lastAm))
|
||||
res.LastAutoMatchTimeUtc = lastAm;
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static EventPartnerStatuePlayfabData Load()
|
||||
{
|
||||
var s = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>().Read(Key).ValueOrThrow();
|
||||
return Deserialize(s);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var s = Serialized();
|
||||
saveStore.Write(Key, s).RequireAccepted();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
using UnityEngine;
|
||||
using asap.core;
|
||||
using System.Collections.Generic;
|
||||
using UniRx;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// 单次加分对应的待播进度条动画参数;队列挂在 Data 单例上,避免 UI Manager 被销毁重建后丢失。
|
||||
/// </summary>
|
||||
public struct EventPartnerStatueTokenExchangePendingUi
|
||||
{
|
||||
public float StartFill;
|
||||
public int OldTicketCount;
|
||||
public int TokenToGive;
|
||||
public float EndProgressRatio;
|
||||
}
|
||||
|
||||
public class EventPartnerStatueTokenExchangeBomb
|
||||
{
|
||||
public int Multiplier { get; set; }
|
||||
}
|
||||
|
||||
public class EventPartnerStatueTokenExchangeFishRaid
|
||||
{
|
||||
public int Multiplier { get; set; }
|
||||
}
|
||||
|
||||
|
||||
// public static class EventPartnerTokenExchangeContextRegistry
|
||||
// {
|
||||
// public static IEventPartnerTokenExchangeContext Context;
|
||||
// }
|
||||
|
||||
// /// <summary>待播 UI 队列有新条目时发布,供 Manager 在激活状态下启动消费协程。</summary>
|
||||
// public class EventPartnerStatueTokenExchangePendingUiEnqueued
|
||||
// {
|
||||
// }
|
||||
|
||||
//- 处理道具获得逻辑
|
||||
|
||||
public class EventPartnerStatueTokenExchangeData
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
#region State
|
||||
private int _eventId = 0;
|
||||
private int _tokenGiven = 0;
|
||||
private int _currentScore = 0;
|
||||
public int EventId => _eventId;
|
||||
public int TokenGiven => _tokenGiven;
|
||||
public int CurrentScore => _currentScore;
|
||||
#endregion
|
||||
|
||||
private readonly Queue<EventPartnerStatueTokenExchangePendingUi> _pendingScoreUi = new Queue<EventPartnerStatueTokenExchangePendingUi>();
|
||||
|
||||
public int PendingScoreUiCount => _pendingScoreUi.Count;
|
||||
public int PendingScoreUiTokenToGive => _pendingScoreUi.Sum(item => item.TokenToGive);
|
||||
|
||||
public void EnqueuePendingScoreUi(in EventPartnerStatueTokenExchangePendingUi item)
|
||||
{
|
||||
_pendingScoreUi.Enqueue(item);
|
||||
}
|
||||
|
||||
public bool TryPeekPendingScoreUi(out EventPartnerStatueTokenExchangePendingUi item)
|
||||
{
|
||||
if (_pendingScoreUi.Count == 0)
|
||||
{
|
||||
item = default;
|
||||
return false;
|
||||
}
|
||||
item = _pendingScoreUi.Peek();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>在对应条目的动画完整播放后调用,从队列移除队首。</summary>
|
||||
public void DequeueConsumedPendingScoreUi()
|
||||
{
|
||||
if (_pendingScoreUi.Count > 0)
|
||||
_pendingScoreUi.Dequeue();
|
||||
}
|
||||
|
||||
public void ClearPendingScoreUiQueue()
|
||||
{
|
||||
_pendingScoreUi.Clear();
|
||||
}
|
||||
|
||||
private CompositeDisposable _exchangeEventDisposables;
|
||||
|
||||
private void EnsureExchangeEventSubscriptions()
|
||||
{
|
||||
if (_exchangeEventDisposables != null)
|
||||
return;
|
||||
_exchangeEventDisposables = new CompositeDisposable();
|
||||
GContext.OnEvent<EventPartnerStatueTokenExchangeBomb>()
|
||||
.Subscribe(OnTokenExchangeBomb)
|
||||
.AddTo(_exchangeEventDisposables);
|
||||
GContext.OnEvent<EventPartnerStatueTokenExchangeFishRaid>()
|
||||
.Subscribe(OnTokenExchangeFishRaid)
|
||||
.AddTo(_exchangeEventDisposables);
|
||||
}
|
||||
|
||||
private void OnTokenExchangeBomb(EventPartnerStatueTokenExchangeBomb evt)
|
||||
{
|
||||
Debug.Log("[EventPartnerStatueTokenExchange] OnTokenExchangeBomb: " + evt.Multiplier);
|
||||
var bombBaseScore = GetBombBaseScore();
|
||||
ApplyTokenExchangeScore(evt.Multiplier * bombBaseScore);
|
||||
}
|
||||
|
||||
private void OnTokenExchangeFishRaid(EventPartnerStatueTokenExchangeFishRaid evt)
|
||||
{
|
||||
Debug.Log("[EventPartnerStatueTokenExchange] OnTokenExchangeFishRaid: " + evt.Multiplier);
|
||||
var fishRaidBaseScore = GetFishRaidBaseScore();
|
||||
ApplyTokenExchangeScore(evt.Multiplier * fishRaidBaseScore);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 逻辑加分并入队待播 UI;与 Manager 解耦,订阅在 <see cref="Load"/> 中建立。
|
||||
/// </summary>
|
||||
private void ApplyTokenExchangeScore(int addedScore)
|
||||
{
|
||||
Debug.Log("[EventPartnerStatueTokenExchange] ApplyTokenExchangeScore: " + addedScore);
|
||||
var context = GContext.container.Resolve<IEventPartnerTokenExchangeContext>();
|
||||
if (context == null)
|
||||
{
|
||||
Debug.LogError("[EventPartnerStatueTokenExchange] token exchange context is null");
|
||||
return;
|
||||
}
|
||||
|
||||
int tokenGivenBefore = _tokenGiven;
|
||||
int currentScoreBefore = _currentScore;
|
||||
float startFill = GetCurrentPercentage();
|
||||
(float progressRatio, int oldTicketCount, int tokenToGive) = OnAddScore(addedScore);
|
||||
if (_tokenGiven == tokenGivenBefore && _currentScore == currentScoreBefore)
|
||||
return;
|
||||
|
||||
EnqueuePendingScoreUi(new EventPartnerStatueTokenExchangePendingUi
|
||||
{
|
||||
StartFill = startFill,
|
||||
OldTicketCount = oldTicketCount,
|
||||
TokenToGive = tokenToGive,
|
||||
EndProgressRatio = progressRatio
|
||||
});
|
||||
|
||||
// RedPointManager.Instance.SetRedPointState(context.RedPointKey, context.TicketCount > 0, context.TicketCount);
|
||||
// GContext.Publish(new EventPartnerStatueTokenExchangePendingUiEnqueued());
|
||||
}
|
||||
|
||||
private float _inflationRate = 1f;
|
||||
private GameCore.PlayerItemData _playerItemData = GContext.container.Resolve<GameCore.PlayerItemData>();
|
||||
|
||||
#region Data provider
|
||||
public float GetCurrentPercentage()
|
||||
{
|
||||
var p = (float)CurrentScore / GetExchangeRequirement(TokenGiven);
|
||||
p = Mathf.Clamp(p, 0f, 1f);
|
||||
return p;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data storage
|
||||
|
||||
public static void Load(cfg.FishingEvent e)
|
||||
{
|
||||
var data = GContext.container.Resolve<EventPartnerStatueTokenExchangeData>();
|
||||
if (data == null)
|
||||
{
|
||||
GContext.container.Register<EventPartnerStatueTokenExchangeData>().AsSingleton();
|
||||
data = GContext.container.Resolve<EventPartnerStatueTokenExchangeData>();
|
||||
}
|
||||
// data.BindContext(EventPartnerTokenExchangeContextRegistry.Context);
|
||||
data.EnsureExchangeEventSubscriptions();
|
||||
var res = data.LoadData();
|
||||
if (!res || data.EventId != e.ID)
|
||||
{
|
||||
data.SetData(e.ID, 0, 0);
|
||||
data.ClearPendingScoreUiQueue();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetData(int eventId, int tokenGiven, int currentScore)
|
||||
{
|
||||
var context = GContext.container.Resolve<IEventPartnerTokenExchangeContext>();
|
||||
_eventId = eventId;
|
||||
_tokenGiven = tokenGiven;
|
||||
_currentScore = currentScore;
|
||||
_inflationRate = context != null ? context.LureInflationRate : 1f;
|
||||
}
|
||||
|
||||
private readonly string PlayfabKey = "EventPartnerStatueTokenExchangeData";
|
||||
|
||||
public void SaveData()
|
||||
{
|
||||
saveStore.Write(PlayfabKey, $"{_eventId}|{_tokenGiven}|{_currentScore}").RequireAccepted();
|
||||
}
|
||||
|
||||
public bool LoadData()
|
||||
{
|
||||
var s = saveStore.Read(PlayfabKey).ValueOrThrow();
|
||||
if (s == null)
|
||||
return false;
|
||||
try
|
||||
{
|
||||
var tokens = s.Split('|');
|
||||
var eventId = int.Parse(tokens[0]);
|
||||
var tokenGiven = int.Parse(tokens[1]);
|
||||
var currentScore = int.Parse(tokens[2]);
|
||||
SetData(eventId, tokenGiven, currentScore);
|
||||
return true;
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Data logic
|
||||
|
||||
public (float progressRatio, int oldTicketCount, int tokenToGive) OnAddScore(int addedScore)
|
||||
{
|
||||
var context = GContext.container.Resolve<IEventPartnerTokenExchangeContext>();
|
||||
if (context == null)
|
||||
{
|
||||
Debug.LogError("[EventPartnerStatueTokenExchange] token exchange context is null");
|
||||
return (0, 0, 0);
|
||||
}
|
||||
(int tokenToGive, float progressRatio, int newScore) = GetProgressDataV2(_tokenGiven, _currentScore, addedScore);
|
||||
_tokenGiven += tokenToGive;
|
||||
GContext.Publish(new game.TargetAddData(context.TokenExchangeItemId, _currentScore, addedScore));
|
||||
_currentScore = newScore;
|
||||
var oldTicketCount = context.TicketCount;
|
||||
context.AddTicket(tokenToGive);
|
||||
SaveData();
|
||||
return (progressRatio, oldTicketCount, tokenToGive);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region TableData
|
||||
private List<int> TokenExchangeStep
|
||||
{
|
||||
get
|
||||
{
|
||||
var context = GContext.container.Resolve<IEventPartnerTokenExchangeContext>();
|
||||
var id = context.TokenExchangeId;
|
||||
return GContext.container.Resolve<cfg.Tables>().TbEventPartnerTokenExchange[id].TokenExchangeStep;
|
||||
}
|
||||
}
|
||||
private List<int> TokenExchangeRequirement
|
||||
{
|
||||
get
|
||||
{
|
||||
var context = GContext.container.Resolve<IEventPartnerTokenExchangeContext>();
|
||||
var id = context.TokenExchangeId;
|
||||
return GContext.container.Resolve<cfg.Tables>().TbEventPartnerTokenExchange[id].TokenExchangeRequirement;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the exchange requirement for the given number of tokens
|
||||
/// </summary>
|
||||
/// <param name="tokenGiven">The number of tokens given</param>
|
||||
/// <returns>The exchange requirement</returns>
|
||||
private int GetExchangeRequirement(int tokenGiven)
|
||||
{
|
||||
var requirement = TokenExchangeRequirement;
|
||||
var step = TokenExchangeStep;
|
||||
|
||||
if (requirement.Count == 0)
|
||||
{
|
||||
Debug.LogError("[EventPartnerStatueTokenExchange] TokenExchangeRequirement is empty");
|
||||
return 100;
|
||||
}
|
||||
|
||||
if (step.Count > 0 && requirement.Count < step.Count + 1)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"[EventPartnerStatueTokenExchange] TokenExchangeRequirement.Count ({requirement.Count}) must be >= TokenExchangeStep.Count + 1 ({step.Count + 1})");
|
||||
return 100;
|
||||
}
|
||||
|
||||
int tierIndex;
|
||||
if (step.Count == 0)
|
||||
tierIndex = 0;
|
||||
else
|
||||
{
|
||||
tierIndex = step.Count;
|
||||
for (int i = 0; i < step.Count; i++)
|
||||
{
|
||||
if (tokenGiven < step[i])
|
||||
{
|
||||
tierIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int res = requirement[tierIndex];
|
||||
res = _playerItemData.IntInflation(res, _inflationRate);
|
||||
if (res <= 0)
|
||||
{
|
||||
Debug.LogError($"[EventPartnerStatueTokenExchange] GetExchangeRequirement returned {res} for tokenGiven {tokenGiven}");
|
||||
return 100;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private (int tokenToGive, float progressRatio, int remainingScore) GetProgressDataV2(int tokenGiven, int currentScore, int scoreToAdd)
|
||||
{
|
||||
int totalScore = currentScore + scoreToAdd;
|
||||
int tokenToGive = 0;
|
||||
|
||||
int requirement = GetExchangeRequirement(tokenGiven);
|
||||
|
||||
// Normalize + process in one loop
|
||||
while (totalScore >= requirement)
|
||||
{
|
||||
totalScore -= requirement;
|
||||
tokenToGive++;
|
||||
|
||||
requirement = GetExchangeRequirement(tokenGiven + tokenToGive);
|
||||
}
|
||||
|
||||
float ratio = (float)totalScore / requirement;
|
||||
return (tokenToGive, ratio, totalScore);
|
||||
}
|
||||
|
||||
public int GetBombBaseScore()
|
||||
{
|
||||
var context = GContext.container.Resolve<IEventPartnerTokenExchangeContext>();
|
||||
return GContext.container.Resolve<cfg.Tables>().TbEventPartnerTokenExchange[context.TokenExchangeId].ExchangeParam[1];
|
||||
}
|
||||
|
||||
public int GetFishRaidBaseScore()
|
||||
{
|
||||
var context = GContext.container.Resolve<IEventPartnerTokenExchangeContext>();
|
||||
return GContext.container.Resolve<cfg.Tables>().TbEventPartnerTokenExchange[context.TokenExchangeId].ExchangeParam[0];
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
using System.Collections.Generic;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using Newtonsoft.Json;
|
||||
using OrderManager;
|
||||
using UnityEngine;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class EventPinataPackDataManager:IOrderReplenishment,IOrderSuccess
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
private readonly EventPinataPackData _kvTarget;
|
||||
private EventPinataPackDataManager(EventPinataPackData target) { _kvTarget=target; }
|
||||
internal static EventPinataPackDataManager CreateForKVLoad()
|
||||
{
|
||||
var target=GContext.container.Resolve<EventPinataPackData>();
|
||||
if(target==null) throw new System.InvalidOperationException("KV Pinata target is not registered.");
|
||||
return new EventPinataPackDataManager(target);
|
||||
}
|
||||
private EventPinataPackData RequireKVTarget()
|
||||
{
|
||||
var target=GContext.container.Resolve<EventPinataPackData>();
|
||||
if(target==null || (_kvTarget!=null && !ReferenceEquals(target,_kvTarget)))
|
||||
throw new System.InvalidOperationException("KV Pinata target changed.");
|
||||
return target;
|
||||
}
|
||||
internal static PlayerDataStorage.PlayerKVPinataState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
PlayerDataStorage.PlayerKVSchemaDiagnostics errors) => PlayerDataStorage.PlayerKVSaveSchema.DecodePinata(raw,errors);
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
var target=RequireKVTarget(); target.EventID=0; target.PinataMainID=0; target.PurchasedCount=0;
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(PlayerDataStorage.PlayerKVPinataState state)
|
||||
{
|
||||
if(state==null) throw new System.ArgumentNullException(nameof(state));
|
||||
var target=RequireKVTarget(); target.EventID=state.EventID; target.PinataMainID=state.PinataMainID; target.PurchasedCount=state.PurchasedCount;
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
private bool m_IsCheckCondition = true;
|
||||
#else
|
||||
private bool m_IsCheckCondition = true;
|
||||
#endif
|
||||
public EventPinataPackDataManager()
|
||||
{
|
||||
GContext.container.Register<EventPinataPackData>().AsSingleton();
|
||||
GetFreshPinataPackData();
|
||||
}
|
||||
public EventPinataPackData GetFreshPinataPackData()
|
||||
{
|
||||
var pinataPack = GContext.container.Resolve<EventPinataPackData>();
|
||||
if (pinataPack == null) return null;
|
||||
|
||||
// 获取当前开放并符合解锁条件的活动ID
|
||||
var fishingEventData = GContext.container.Resolve<FishingEventData>();
|
||||
|
||||
var fishingEvent = fishingEventData.GetEventByTypeAndSubType(9, 6, m_IsCheckCondition);
|
||||
if (fishingEvent == null) return pinataPack;
|
||||
var pinataPackID = fishingEvent.RedirectID;
|
||||
|
||||
if (pinataPackID < 0) return pinataPack;
|
||||
|
||||
// 获取当前活动数据
|
||||
var tbEventData = GContext.container.Resolve<Tables>().TbEventPinataPackMain;
|
||||
var pinataPackMain = tbEventData?.GetOrDefault(pinataPackID);
|
||||
if (pinataPackMain == null) return pinataPack;
|
||||
|
||||
var isEquelEvent = fishingEvent.ID == pinataPack.EventID;
|
||||
if (isEquelEvent) return pinataPack;
|
||||
pinataPack.EventID = fishingEvent.ID;
|
||||
pinataPack.PinataMainID=fishingEvent.RedirectID;
|
||||
pinataPack.PurchasedCount = 0;
|
||||
if (!pinataPack.IsActive) return pinataPack;
|
||||
var packMain = GContext.container.Resolve<EventPinataPackData>()?.EventPinataPackMain;
|
||||
UITypes.PinataPack_PonyPanel.SetType(packMain?.UIPanel?? nameof(PinataPack_PonyPanel));
|
||||
GContext.container.Resolve<IFaceUIService>().AddGiftFaceUI(fishingEvent.ID, UITypes.PinataPack_PonyPanel, 0);
|
||||
|
||||
return pinataPack;
|
||||
}
|
||||
public EventPinataPackMain GetCurrentEventPinataPackMain()
|
||||
{
|
||||
var pinataPackData= GContext.container.Resolve<EventPinataPackData>(); ;
|
||||
if (pinataPackData == null) return null;
|
||||
var tables = GContext.container.Resolve<Tables>();
|
||||
return tables.TbEventPinataPackMain.GetOrDefault(pinataPackData.PinataMainID);
|
||||
}
|
||||
public void LoadData(string sData)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd EventPinataPackData LoadData sData {sData}");
|
||||
#endif
|
||||
var data = JsonConvert.DeserializeObject<EventPinataPackData>(sData);
|
||||
if (data == null) return;
|
||||
var pinataPackData = GContext.container.Resolve<EventPinataPackData>();
|
||||
if (pinataPackData == null)
|
||||
{
|
||||
GContext.container.Register<EventPinataPackData>().AsSingleton();
|
||||
GContext.container.Resolve<EventPinataPackData>().SetData(data);
|
||||
}
|
||||
else pinataPackData.SetData(data);
|
||||
|
||||
GetFreshPinataPackData();
|
||||
}
|
||||
public void SaveData()
|
||||
{
|
||||
var pinataPackData = GContext.container.Resolve<EventPinataPackData>();
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"jsd SaveData {JsonConvert.SerializeObject(pinataPackData)}");
|
||||
#endif
|
||||
SaveStore.Write(nameof(EventPinataPackData), JsonConvert.SerializeObject(pinataPackData)).RequireAccepted();
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="packData">data</param>
|
||||
/// <param name="packMain">config packMain</param>
|
||||
/// <param name="packConfig1">购买时的礼包</param>
|
||||
/// <param name="packConfig2">点击小马掉落的礼包</param>
|
||||
/// <param name="eventPackManager">config eventPackManager</param>
|
||||
/// <returns></returns>
|
||||
public static bool GetData(out EventPinataPackData packData, out EventPinataPackMain packMain, out Pack packConfig_purchase, out int clickDropId, out EventPackManager eventPackManager)
|
||||
{
|
||||
packMain = null;
|
||||
packConfig_purchase = null;
|
||||
clickDropId = 0;
|
||||
eventPackManager = null;
|
||||
|
||||
packData = GContext.container.Resolve<EventPinataPackData>();
|
||||
|
||||
packMain = packData?.EventPinataPackMain;
|
||||
if (packMain == null)
|
||||
{
|
||||
Debug.LogError($" [PinataPack_PonyPanel] GetData packMain is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
var vipLevel = GContext.container.Resolve<PlayerData>().PriceLv;
|
||||
|
||||
eventPackManager = GContext.container.Resolve<Tables>().TbEventPackManager?.GetOrDefault(packMain.PackId);
|
||||
if (eventPackManager == null)
|
||||
{
|
||||
Debug.LogError($"packMain.PackId 配置错误 packMain id: {packMain.ID}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var packIds = eventPackManager.VIPPackList[vipLevel];
|
||||
|
||||
packConfig_purchase = GContext.container.Resolve<Tables>().TbPack?.GetOrDefault(packIds[0]);
|
||||
if (packConfig_purchase == null)
|
||||
{
|
||||
Debug.LogError($"eventPackManager.VIPPackList 配置错误 VIPPackList index 0 -> id: {packIds[0]}");
|
||||
return false;
|
||||
}
|
||||
var itemDatas = GContext.container.Resolve<PlayerItemData>().GetItemDataByDropIdAndTypeLureInflation(null, packConfig_purchase.DropID);
|
||||
foreach (var itemData in itemDatas)
|
||||
{
|
||||
var itemTmepConfig = GContext.container.Resolve<Tables>()?.TbItem.GetOrDefault(itemData.id);
|
||||
if (itemTmepConfig?.Type == 9 && itemTmepConfig?.SubType == 9)
|
||||
{
|
||||
clickDropId = itemTmepConfig.RedirectID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnReplenish(ShopBuyTypeData buyType, IAPItemList iAPItemList, List<ItemData> itemDataGift, RewardType rewardShowType, bool voucher = false)
|
||||
{
|
||||
if(buyType.extraPurcheData==null) return;
|
||||
if ( !buyType.extraPurcheData.TryGetValue(nameof(EventPinataPackData), out var value)) return;
|
||||
// var clickDropId =(int) value;
|
||||
//GContext.container.Resolve<PlayerItemData>().AddItemByDropLureInflation(null, clickDropId,null);
|
||||
//判断是不是同一期活动
|
||||
if(!buyType.extraPurcheData.TryGetValue("EventID",out var eventIdjToken)) return;
|
||||
var eventID=(int) eventIdjToken;
|
||||
var data= GContext.container.Resolve<EventPinataPackData>();
|
||||
if (eventID != data.EventID)
|
||||
{
|
||||
//todo 保底机制
|
||||
return;
|
||||
}
|
||||
data?.AddPurchasedCount(1);
|
||||
}
|
||||
|
||||
public void OnOrderSuccess(ShopBuyTypeData buyType, IAPItemList iAPItemList, List<ItemData> itemDataGift, RewardType rewardShowType,
|
||||
bool voucher = false)
|
||||
{
|
||||
if(buyType.extraPurcheData==null) return;
|
||||
if ( !buyType.extraPurcheData.TryGetValue(nameof(EventPinataPackData), out var value)) return;
|
||||
//判断是不是同一期活动
|
||||
if(!buyType.extraPurcheData.TryGetValue("EventID",out var eventIdjToken)) return;
|
||||
var eventID=(int) eventIdjToken;
|
||||
var data= GContext.container.Resolve<EventPinataPackData>();
|
||||
if (eventID != data.EventID)
|
||||
{
|
||||
//todo 保底机制
|
||||
return;
|
||||
}
|
||||
data?.AddPurchasedCount(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
using asap.core;
|
||||
using game;
|
||||
using GameCore;
|
||||
using System.Text;
|
||||
|
||||
namespace PinballUncountable
|
||||
{
|
||||
public class Manager : IHasMultiplier
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
private int _score, _ticketCount, _hitCountLeft, _hitCountRight;
|
||||
private float _inflationRate = 0f;
|
||||
private IEventAggregator _eventAggregator = new EventAggregator();
|
||||
private TableContext _tableContext;
|
||||
public IEventAggregator EventAggregator => _eventAggregator;
|
||||
public TableContext TableContext => _tableContext;
|
||||
public int HitCount1 => _hitCountLeft;
|
||||
public int HitCount2 => _hitCountRight;
|
||||
|
||||
public float InflationRate => _inflationRate;
|
||||
public int Score => _score;
|
||||
public readonly string RedPointKey = "pinball_uncountable";
|
||||
|
||||
#region Initiation
|
||||
public static void InitEvent(cfg.FishingEvent e)
|
||||
{
|
||||
var manager = GContext.container.Resolve<Manager>();
|
||||
if (manager == null)
|
||||
{
|
||||
GContext.container.Register<Manager>().AsSingleton();
|
||||
manager = GContext.container.Resolve<Manager>();
|
||||
}
|
||||
var loadRes = manager.Load();
|
||||
if (!loadRes || e.ID != manager.TableContext.EventId)
|
||||
{
|
||||
manager.Init(e.ID);
|
||||
manager.SavePlayfab();
|
||||
}
|
||||
UITypes.PinballUncountablePanel.SetType(manager.TableContext.GetPanelUrl());
|
||||
UITypes.PinballUncountableNormalPackPanel.SetType(manager.TableContext.GetNormalPackPanelUrl());
|
||||
UITypes.PinballUncountableInfoPanel.SetType(manager.TableContext.GetInfoPanelUrl());
|
||||
}
|
||||
|
||||
public void Init(int eventId)
|
||||
{
|
||||
_tableContext = new TableContext(eventId);
|
||||
_score = 0;
|
||||
_ticketCount = _tableContext.GetInitWelcomeGift();
|
||||
_hitCountLeft = 0;
|
||||
_hitCountRight = 0;
|
||||
_inflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
}
|
||||
|
||||
public bool Load()
|
||||
{
|
||||
return Deserialize(saveStore.Read(_playfabKey).ValueOrThrow());
|
||||
}
|
||||
|
||||
public void InitTest()
|
||||
{
|
||||
_eventAggregator = new EventAggregator();
|
||||
_tableContext = TableContext.InitTest();
|
||||
_score = 0;
|
||||
_ticketCount = 5555;
|
||||
_hitCountLeft = 0;
|
||||
_hitCountRight = 0;
|
||||
_inflationRate = GContext.container.Resolve<PlayerData>().InflationRate;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region System
|
||||
|
||||
public void OnBallCreate(int hit1, int hit2, int exitIdx)
|
||||
{
|
||||
int hitCountLeftNew = _hitCountLeft + hit1;
|
||||
int hitCountRightNew = _hitCountRight + hit2;
|
||||
int scoreNew = _score + _tableContext.GetPointMagFromExitIndex(exitIdx);
|
||||
|
||||
TryGrantDiscReward(0, _hitCountLeft, hitCountLeftNew);
|
||||
TryGrantDiscReward(1, _hitCountRight, hitCountRightNew);
|
||||
TryGrantProgressReward(_score, scoreNew);
|
||||
|
||||
|
||||
|
||||
_hitCountLeft = hitCountLeftNew;
|
||||
_hitCountRight = hitCountRightNew;
|
||||
_score = scoreNew;
|
||||
// UnityEngine.Debug.Log($"[PinballUncountable] ScoreRecord @ {exitIdx}: {_hitCountLeft}, {_hitCountRight}, {_score}");
|
||||
SavePlayfab();
|
||||
}
|
||||
|
||||
public void AddScore(int score2Add)
|
||||
{
|
||||
_score += score2Add;
|
||||
SavePlayfab();
|
||||
}
|
||||
|
||||
public bool AddTicket(int ticket2Add)
|
||||
{
|
||||
if (_ticketCount + ticket2Add < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_ticketCount += ticket2Add;
|
||||
EventAggregator.Publish(new PinballUncountable.EventTicketUpdate());
|
||||
RedPointManager.Instance.SetRedPointState(RedPointKey, _ticketCount > 0, _ticketCount);
|
||||
SavePlayfab();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void TryGrantDiscReward(int idx, int hitCountOld, int hitCountNew)
|
||||
{
|
||||
var dropList = TableContext.GetRewardDropIdListFromHitCount(idx, hitCountOld, hitCountNew);
|
||||
var changeSource = new ChangeSource(TableContext.EventId, "PinballUncountable", "Disc", "PinballUncountable");
|
||||
// UnityEngine.Debug.Log($"[PinballUncountable] TryGrantDiscReward @{InflationRate}: {string.Join(",", dropList)}");
|
||||
GContext.Publish(new DeferredRewardStashService.EventStashDropList
|
||||
{
|
||||
DropList = dropList,
|
||||
changeSource = changeSource,
|
||||
Inflate = InflationRate
|
||||
});
|
||||
|
||||
// int evEventId, evRound, evRewardId;
|
||||
// evEventId = TableContext.EventId;
|
||||
// evRound = -1;
|
||||
// for (int i = 0; i < dropList.Count; i++)
|
||||
// {
|
||||
// evRewardId = dropList[i];
|
||||
// UnityEngine.Debug.Log($"[PinballUncountable] EventTrack: Reward {evEventId}, {evRound}, {evRewardId}");
|
||||
// #if AGG
|
||||
// using (var e = GEvent.GameEvent("event_pinballuncountable_reward"))
|
||||
// {
|
||||
// e.AddContent("event_id", evEventId)
|
||||
// .AddContent("round", evRound)
|
||||
// .AddContent("reward_id", evRewardId);
|
||||
// }
|
||||
// #endif
|
||||
// }
|
||||
}
|
||||
|
||||
private void TryGrantProgressReward(int oldScore, int newScore)
|
||||
{
|
||||
var (dropList, roundList) = TableContext.GetRewardDropIdListFromScore(oldScore, newScore);
|
||||
var changeSource = new ChangeSource(TableContext.EventId, "PinballUncountable", "Score", "PinballUncountable");
|
||||
// UnityEngine.Debug.Log($"[PinballUncountable] TryGrantProgressReward @{InflationRate}: {string.Join(",", dropList)}");
|
||||
GContext.Publish(new DeferredRewardStashService.EventStashDropList
|
||||
{
|
||||
DropList = dropList,
|
||||
changeSource = changeSource,
|
||||
Inflate = InflationRate
|
||||
});
|
||||
|
||||
int evEventId, evRound, evRewardId;
|
||||
evEventId = TableContext.EventId;
|
||||
int oldMaxScore = TableContext.GetCurrentMaxScore(oldScore);
|
||||
int newMaxScore = TableContext.GetCurrentMaxScore(newScore);
|
||||
|
||||
for (int i = 0; i < dropList.Count; i++)
|
||||
{
|
||||
evRound = roundList[i];
|
||||
evRewardId = dropList[i];
|
||||
// UnityEngine.Debug.Log($"[PinballUncountable] EventTrack: Reward {evEventId}, {evRound}, {evRewardId}");
|
||||
#if AGG
|
||||
using (var e = GEvent.GameEvent("event_pinballuncountable_reward"))
|
||||
{
|
||||
e.AddContent("event_id", evEventId)
|
||||
.AddContent("round", evRound)
|
||||
.AddContent("reward_id", evRewardId);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get Data
|
||||
public int GetHitCountByIdx(int idx)
|
||||
{
|
||||
if (idx == 0)
|
||||
return _hitCountLeft;
|
||||
else if (idx == 1)
|
||||
return _hitCountRight;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int GetCurrentMaxMultiplier()
|
||||
{
|
||||
var multipliers = _tableContext.GetMultiplierArray();
|
||||
for (int i = multipliers.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (multipliers[i] > _ticketCount)
|
||||
continue;
|
||||
return multipliers[i];
|
||||
}
|
||||
return multipliers[0];
|
||||
}
|
||||
|
||||
public bool IsWithinTime()
|
||||
{
|
||||
(var startTime, var endTime) = FtUtils.GetEventTimeLimit(TableContext.EventId);
|
||||
return startTime <= ZZTimeHelper.UtcNow() && ZZTimeHelper.UtcNow() <= endTime;
|
||||
}
|
||||
|
||||
public PinballUncountableEntranceButtonData GetEntranceData()
|
||||
{
|
||||
var data = new PinballUncountableEntranceButtonData();
|
||||
data.TimerContext = _tableContext.GetTimerContext();
|
||||
data.IconUrl = _tableContext.GetIconUrl();
|
||||
return data;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataStorage
|
||||
private readonly string _playerPreferencesKey = "PinballUncountable" + GContext.container.Resolve<IUserService>().UserId;
|
||||
private readonly string _playfabKey = "PinballUncountable";
|
||||
private readonly char _separator = ',';
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(TableContext.EventId).Append(_separator);
|
||||
sb.Append(_score).Append(_separator);
|
||||
sb.Append(_ticketCount).Append(_separator);
|
||||
sb.Append(_hitCountLeft).Append(_separator);
|
||||
sb.Append(_hitCountRight).Append(_separator);
|
||||
sb.Append(_inflationRate);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public bool Deserialize(string data)
|
||||
{
|
||||
if (data == null || data == "")
|
||||
return false;
|
||||
var arr = data.Trim(_separator).Split(_separator);
|
||||
if (arr.Length != 6)
|
||||
{
|
||||
UnityEngine.Debug.LogError("[PinballUncountable]Data Corrupted.");
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
_tableContext = new TableContext(int.Parse(arr[0]));
|
||||
_score = int.Parse(arr[1]);
|
||||
_ticketCount = int.Parse(arr[2]);
|
||||
_hitCountLeft = int.Parse(arr[3]);
|
||||
_hitCountRight = int.Parse(arr[4]);
|
||||
_inflationRate = float.Parse(arr[5]);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError(e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SavePlayfab()
|
||||
{
|
||||
// UnityEngine.Debug.Log("[PinballUncountable]Save Data.");
|
||||
saveStore.Write(_playfabKey, Serialize()).RequireAccepted();
|
||||
}
|
||||
|
||||
public void SavePlayerPreferences(int multiplier)
|
||||
{
|
||||
UnityEngine.PlayerPrefs.SetInt(_playerPreferencesKey, multiplier);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Multiplier
|
||||
public int GetTicketCount()
|
||||
{
|
||||
return _ticketCount;
|
||||
}
|
||||
|
||||
public int[] GetMultiplierArray()
|
||||
{
|
||||
return _tableContext.GetMultiplierArray();
|
||||
}
|
||||
|
||||
public int GetMultiplier()
|
||||
{
|
||||
return UnityEngine.PlayerPrefs.GetInt(_playerPreferencesKey, -1);
|
||||
}
|
||||
|
||||
public void SetMultiplier(int multiplier)
|
||||
{
|
||||
SavePlayerPreferences(multiplier);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using TMPro;
|
||||
using tysdk;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class BindAccountPopupPanel : MonoBehaviour
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
IUserService userService;
|
||||
Button btn_close;
|
||||
Button btn_close1;
|
||||
|
||||
public BindAccountBtn bind_facebookbtn;
|
||||
|
||||
public BindAccountBtn bind_googlebtn;
|
||||
|
||||
public BindAccountBtn bind_applebtn;
|
||||
|
||||
public BindAccountBtn bind_vkbtn;
|
||||
|
||||
int rewardID;
|
||||
ItemData itemData;
|
||||
private void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
userService = GContext.container.Resolve<IUserService>();
|
||||
btn_close1 = transform.Find("btn_close").GetComponent<Button>();
|
||||
btn_close = transform.Find("mask").GetComponent<Button>();
|
||||
|
||||
bind_googlebtn = transform.Find("root/layout/btn_google").GetComponent<BindAccountBtn>();
|
||||
bind_facebookbtn = transform.Find("root/layout/btn_facebook").GetComponent<BindAccountBtn>();
|
||||
bind_applebtn = transform.Find("root/layout/btn_apple").GetComponent<BindAccountBtn>();
|
||||
bind_vkbtn = transform.Find("root/layout/btn_vk").GetComponent<BindAccountBtn>();
|
||||
|
||||
#if UNITY_IOS
|
||||
bind_googlebtn.gameObject.SetActive(false);
|
||||
bind_vkbtn.gameObject.SetActive(false);
|
||||
bind_applebtn.gameObject.SetActive(true);
|
||||
bind_facebookbtn.gameObject.SetActive(true);
|
||||
#else
|
||||
bind_googlebtn.gameObject.SetActive(ChannelManager.IsGooglePlay);
|
||||
bind_facebookbtn.gameObject.SetActive(ChannelManager.IsGooglePlay);
|
||||
bind_vkbtn.gameObject.SetActive(ChannelManager.IsRustoreStore);
|
||||
bind_applebtn.gameObject.SetActive(false);
|
||||
#endif
|
||||
}
|
||||
private void Start()
|
||||
{
|
||||
rewardID = GContext.container.Resolve<Tables>().TbGlobalConfig.AccountLinkReward;
|
||||
itemData = GContext.container.Resolve<PlayerItemData>().GetItemDataOne(rewardID);
|
||||
btn_close.onClick.AddListener(OnClose);
|
||||
btn_close1.onClick.AddListener(OnClose);
|
||||
|
||||
bind_facebookbtn.btn.interactable = false;
|
||||
bind_facebookbtn.rewardPanel.SetActive(false);
|
||||
bind_facebookbtn.btn.onClick.AddListener(OnBtnFacebook);
|
||||
|
||||
bind_googlebtn.btn.interactable = false;
|
||||
bind_googlebtn.rewardPanel.SetActive(false);
|
||||
bind_googlebtn.btn.onClick.AddListener(OnBtnGoogle);
|
||||
|
||||
bind_applebtn.btn.interactable = false;
|
||||
bind_applebtn.rewardPanel.SetActive(false);
|
||||
bind_applebtn.btn.onClick.AddListener(OnBtnApple);
|
||||
|
||||
bind_vkbtn.btn.interactable = false;
|
||||
bind_vkbtn.rewardPanel.SetActive(false);
|
||||
bind_vkbtn.btn.onClick.AddListener(OnBtnVK);
|
||||
SetMaterial();
|
||||
}
|
||||
|
||||
void SetMaterial()
|
||||
{
|
||||
bool isLinkGoogle = false;
|
||||
bool isLinkFacebook = false;
|
||||
bool isLinkApple = false;
|
||||
bool isLinkVk = false;
|
||||
|
||||
if (TYSdkFacade.TYAccountInfo != null)
|
||||
{
|
||||
isLinkGoogle = TYSdkFacade.TYAccountInfo.linkedAccout.Contains(EAccoutType.hwGoogle);
|
||||
isLinkFacebook = TYSdkFacade.TYAccountInfo.linkedAccout.Contains(EAccoutType.hwFacebook);
|
||||
isLinkApple = TYSdkFacade.TYAccountInfo.linkedAccout.Contains(EAccoutType.Apple);
|
||||
isLinkVk = TYSdkFacade.TYAccountInfo.linkedAccout.Contains(EAccoutType.hwVkid);
|
||||
}
|
||||
|
||||
Item item = GContext.container.Resolve<Tables>().TbItem.GetOrDefault(itemData.id);
|
||||
|
||||
IUIService uIService = GContext.container.Resolve<IUIService>();
|
||||
if (!isLinkGoogle)
|
||||
{
|
||||
bind_googlebtn.rewardText.text = itemData.count.ToString();
|
||||
uIService.SetImageSprite(bind_googlebtn.p_icon, item.Icon, BasePanel.PanelName);
|
||||
}
|
||||
if (!isLinkFacebook)
|
||||
{
|
||||
bind_facebookbtn.rewardText.text = itemData.count.ToString();
|
||||
uIService.SetImageSprite(bind_facebookbtn.p_icon, item.Icon, BasePanel.PanelName);
|
||||
}
|
||||
|
||||
if (!isLinkApple)
|
||||
{
|
||||
bind_applebtn.rewardText.text = itemData.count.ToString();
|
||||
uIService.SetImageSprite(bind_applebtn.p_icon, item.Icon, BasePanel.PanelName);
|
||||
}
|
||||
|
||||
if (!isLinkVk)
|
||||
{
|
||||
bind_vkbtn.rewardText.text = itemData.count.ToString();
|
||||
uIService.SetImageSprite(bind_vkbtn.p_icon, item.Icon, BasePanel.PanelName);
|
||||
}
|
||||
|
||||
bind_googlebtn.btn.interactable = !isLinkGoogle;
|
||||
bind_facebookbtn.btn.interactable = !isLinkFacebook;
|
||||
bind_applebtn.btn.interactable = !isLinkApple;
|
||||
bind_vkbtn.btn.interactable = !isLinkVk;
|
||||
|
||||
// bind_googlebtn.rewardPanel.SetActive(!isLinkGoogle);
|
||||
// bind_facebookbtn.rewardPanel.SetActive(!isLinkFacebook);
|
||||
// bind_applebtn.rewardPanel.SetActive(!isLinkApple);
|
||||
// bind_vkbtn.rewardPanel.SetActive(!isLinkVk);
|
||||
bind_googlebtn.rewardPanel.SetActive(false);
|
||||
bind_facebookbtn.rewardPanel.SetActive(false);
|
||||
bind_applebtn.rewardPanel.SetActive(false);
|
||||
bind_vkbtn.rewardPanel.SetActive(false);
|
||||
}
|
||||
|
||||
async void OnBtnGoogle()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (await OnLinkSns(EAccoutType.hwGoogle))
|
||||
{
|
||||
bind_googlebtn.btn.interactable = false;
|
||||
bind_googlebtn.rewardPanel.SetActive(false);
|
||||
GetReward();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning(e);
|
||||
}
|
||||
}
|
||||
|
||||
async void OnBtnFacebook()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (await OnLinkSns(EAccoutType.hwFacebook))
|
||||
{
|
||||
bind_facebookbtn.btn.interactable = false;
|
||||
bind_facebookbtn.rewardPanel.SetActive(false);
|
||||
GetReward();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning(e);
|
||||
}
|
||||
}
|
||||
|
||||
async void OnBtnApple()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (await OnLinkSns(EAccoutType.Apple))
|
||||
{
|
||||
bind_applebtn.btn.interactable = false;
|
||||
bind_applebtn.rewardPanel.SetActive(false);
|
||||
GetReward();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning(e);
|
||||
}
|
||||
}
|
||||
|
||||
async void OnBtnVK()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (await OnLinkSns(EAccoutType.hwVkid))
|
||||
{
|
||||
bind_vkbtn.btn.interactable = false;
|
||||
bind_vkbtn.rewardPanel.SetActive(false);
|
||||
GetReward();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning(e);
|
||||
}
|
||||
}
|
||||
|
||||
async Task<bool> OnLinkSns(EAccoutType accountType)
|
||||
{
|
||||
try
|
||||
{
|
||||
UIManager.ShowWaitingBlock("BindAccountPopupPanel::OnlinkSns");
|
||||
var (code, userid) = await userService.LinkAccount(accountType);
|
||||
if (this == null || !saveStore.IsCurrent) return false;
|
||||
|
||||
if (code == 0)
|
||||
{
|
||||
saveStore.Write(accountType.ToString(), "1").RequireAccepted();
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_BindAccountPopupPanel_101007"));
|
||||
}
|
||||
else if (code == -1 && userid != int.Parse(userService.CustomId))
|
||||
{
|
||||
var config = GContext.container.Resolve<IConfig>();
|
||||
var titleId = config.Get<string>(GConstant.K_PlayFab_Title, GConstant.V_Debug_PF_Title);
|
||||
var info = await PlayFabTools.GetPFPlayerInfoBySDKId(userid, titleId);
|
||||
if (this == null || !saveStore.IsCurrent) return false;
|
||||
if (!info.IsAccountValid || !info.IsDataValid)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"BindAccountPopupPanel: Get Account Info Failed, isAccountValid: {info.IsAccountValid}, isDataValid: {info.IsDataValid}");
|
||||
var msg = LocalizationMgr.GetText("UI_BindAccountPopupPanel_101008");
|
||||
ToastPanel.Show($"{msg} [{code}]");
|
||||
}
|
||||
|
||||
var switchPanelGo = await UIManager.Instance.GetUIAsync(UITypes.AccountSwitchPopupPanel);
|
||||
if (this == null || !saveStore.IsCurrent) return false;
|
||||
switchPanelGo.GetComponent<AccountSwitchPopupPanel>()
|
||||
.SetData(info.DisplayName, info.Avatar, info.Level, userid, accountType);
|
||||
UIManager.Instance.DestroyUI(UITypes.BindAccountPopupPanel);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"BindAccountPopupPanel: Link Account Failed, code: {code}");
|
||||
var msg = LocalizationMgr.GetText("UI_BindAccountPopupPanel_101008");
|
||||
ToastPanel.Show($"{msg} [{code}]");
|
||||
}
|
||||
|
||||
return code == 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var msg = LocalizationMgr.GetText("UI_BindAccountPopupPanel_101008");
|
||||
ToastPanel.Show($"{msg} [{e.Message}]");
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
UIManager.HideWaitingBlock("BindAccountPopupPanel::OnlinkSns");
|
||||
}
|
||||
}
|
||||
|
||||
void GetReward()
|
||||
{
|
||||
// GContext.container.Resolve<PlayerItemData>().AddItem(itemData, null);
|
||||
// GContext.Publish(new ShowData(itemData));
|
||||
// GContext.Publish(new ShowData());
|
||||
// GContext.container.Resolve<PlayerShopData>().GetSKUs();
|
||||
}
|
||||
protected virtual void OnClose()
|
||||
{
|
||||
UIManager.Instance.DestroyUI(UITypes.BindAccountPopupPanel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using game;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using tysdk;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class PlayerHeadPopupPanel : MonoBehaviour
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
IUserService userService;
|
||||
public Button btn_mask;
|
||||
public Button btn_close;
|
||||
public Head head;
|
||||
public TMP_Text text_name;
|
||||
public Button btn_name;
|
||||
public Button btn_save;
|
||||
public Transform content;
|
||||
public GameObject item;
|
||||
string url;
|
||||
string headUrl;
|
||||
int index;
|
||||
List<HeadItem> platformHead = new List<HeadItem>();
|
||||
List<HeadItem> headItems = new List<HeadItem>();
|
||||
List<UserAvatar> userAvatars = new List<UserAvatar>();
|
||||
HeadItem headItem_fb;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
userAvatars = GContext.container.Resolve<Tables>().TbUserAvatar.DataList;
|
||||
userService = GContext.container.Resolve<IUserService>();
|
||||
btn_mask = transform.Find("mask").GetComponent<Button>();
|
||||
btn_close = transform.Find("btn_close").GetComponent<Button>();
|
||||
head = transform.Find("root/btn_head").GetComponent<Head>();
|
||||
text_name = transform.Find("root/btn_head/text_name").GetComponent<TMP_Text>();
|
||||
btn_name = transform.Find("root/btn_head/text_name/btn_change").GetComponent<Button>();
|
||||
btn_save = transform.Find("root/btn_save/btn_green").GetComponent<Button>();
|
||||
content = transform.Find("root/ScrollView1/Viewport/Content/Item/panel_head").GetComponent<Transform>();
|
||||
item = transform.Find("root/ScrollView1/Viewport/Content/Item/panel_head/head").gameObject;
|
||||
GContext.container.Resolve<GuideDataCenter>().InspectTriggerGuide("PlayerHeadPopupPanel");
|
||||
}
|
||||
private void Start()
|
||||
{
|
||||
item.SetActive(false);
|
||||
text_name.text = userService.DisplayName;
|
||||
url = headUrl = userService.AvatarUrl;
|
||||
btn_save.onClick.AddListener(OnClickSave);
|
||||
btn_name.onClick.AddListener(OnClickName);
|
||||
btn_mask.onClick.AddListener(OnClickClose);
|
||||
btn_close.onClick.AddListener(OnClickClose);
|
||||
|
||||
|
||||
index = 0;
|
||||
|
||||
platformHead = new List<HeadItem>();
|
||||
#region facebookAvatar
|
||||
if (!ChannelManager.IsRustoreStore)
|
||||
{
|
||||
SetHeadItemFb();
|
||||
}
|
||||
#endregion facebookAvatar
|
||||
|
||||
for (int i = 0; i < userAvatars.Count; i++)
|
||||
{
|
||||
GameObject gameObject = Instantiate(item, content);
|
||||
gameObject.SetActive(true);
|
||||
HeadItem headItem = gameObject.AddComponent<HeadItem>();
|
||||
headItem.SetSelected(false);
|
||||
string avatar = userAvatars[i].Avatar;
|
||||
int clickindex = i;
|
||||
headItem.onClick = () => { ClickItem(clickindex, avatar); };
|
||||
|
||||
headItems.Add(headItem);
|
||||
if (string.IsNullOrEmpty(headUrl))
|
||||
{
|
||||
if (userAvatars[i].ID == GContext.container.Resolve<Tables>().TbGlobalConfig.InitAvatar)
|
||||
{
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (userAvatars[i].Avatar == headUrl)
|
||||
{
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index >= headItems.Count)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
if (index >= 0)
|
||||
{
|
||||
headItems[index].SetSelected(true);
|
||||
url = userAvatars[index].Avatar;
|
||||
}
|
||||
|
||||
head.SetData(url);
|
||||
for (int i = 0; i < userAvatars.Count; i++)
|
||||
{
|
||||
headItems[i].SetUrlAsync(userAvatars[i].Avatar);
|
||||
}
|
||||
}
|
||||
void SetHeadItemFb()
|
||||
{
|
||||
GameObject head_fb = Instantiate(item, content);
|
||||
head_fb.SetActive(true);
|
||||
headItem_fb = head_fb.AddComponent<HeadItem>();
|
||||
headItem_fb.SetSelected(false);
|
||||
headItem_fb.SetUrlAsync("sp_avatar_33");
|
||||
platformHead.Add(headItem_fb);
|
||||
headItem_fb.onClick = OnBtnFacebook;
|
||||
|
||||
Dictionary<string, string> EAccoutUrl = userService.EAccoutUrl;
|
||||
if (EAccoutUrl != null && EAccoutUrl.Count > 0)
|
||||
{
|
||||
//平台头像
|
||||
if (EAccoutUrl.TryGetValue(EAccoutType.hwFacebook.ToString(), out string facebookAvatar))
|
||||
{
|
||||
headItem_fb.SetUrlAsync(facebookAvatar);
|
||||
headItem_fb.SetSelected(url == facebookAvatar);
|
||||
if (url == facebookAvatar)
|
||||
{
|
||||
index = -1;
|
||||
}
|
||||
headItem_fb.onClick = () => { ClickItem(-1, facebookAvatar); };
|
||||
headItem_fb.transform.Find("tag_facebook").gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
async void OnClickName()
|
||||
{
|
||||
btn_name.enabled = false;
|
||||
GameObject panel = await UIManager.Instance.GetUIAsync(UITypes.PlayerNamePopupPanel);
|
||||
PlayerNamePopupPanel playerNamePopupPanel = panel.GetComponent<PlayerNamePopupPanel>();
|
||||
//playerNamePopupPanel.inputField.text = text_name.text;
|
||||
playerNamePopupPanel.Show(text_name);
|
||||
btn_name.enabled = true;
|
||||
}
|
||||
void ClickItem(int _index, string avatar)
|
||||
{
|
||||
if (index == _index)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (index >= 0)
|
||||
{
|
||||
headItems[index].SetSelected(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
platformHead[-index - 1].SetSelected(false);
|
||||
}
|
||||
index = _index;
|
||||
if (index >= 0)
|
||||
{
|
||||
headItems[index].SetSelected(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
platformHead[-index - 1].SetSelected(true);
|
||||
}
|
||||
url = avatar;
|
||||
head.SetData(url);
|
||||
}
|
||||
void OnClickSave()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(url) && headUrl != url)
|
||||
{
|
||||
userService.UpdateAvatarUrl(url);
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_13"));
|
||||
}
|
||||
OnClickClose();
|
||||
}
|
||||
void OnClickClose()
|
||||
{
|
||||
UIManager.Instance.DestroyUI(UITypes.PlayerHeadPopupPanel);
|
||||
}
|
||||
|
||||
async void OnBtnFacebook()
|
||||
{
|
||||
if (TYSdkFacade.TYAccountInfo != null)
|
||||
{
|
||||
bool isLinkFacebook = TYSdkFacade.TYAccountInfo.linkedAccout.Contains(EAccoutType.hwFacebook);
|
||||
if (isLinkFacebook)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
var result = await userService.LinkAccount(EAccoutType.hwFacebook);
|
||||
if (this == null || !saveStore.IsCurrent) return;
|
||||
if (result.code == 0)
|
||||
{
|
||||
saveStore.Write(EAccoutType.hwFacebook.ToString(), "1").RequireAccepted();
|
||||
Dictionary<string, string> EAccoutUrl = userService.EAccoutUrl;
|
||||
if (EAccoutUrl != null && EAccoutUrl.Count > 0)
|
||||
{
|
||||
if (EAccoutUrl.TryGetValue(EAccoutType.hwFacebook.ToString(), out string facebookAvatar))
|
||||
{
|
||||
headItem_fb.SetUrlAsync(facebookAvatar);
|
||||
headItem_fb.onClick = () => { ClickItem(-1, facebookAvatar); };
|
||||
headItem_fb.transform.Find("tag_facebook").gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
GetReward();
|
||||
}
|
||||
}
|
||||
void GetReward()
|
||||
{
|
||||
IFootPrintService footPrintService = GContext.container.Resolve<IFootPrintService>();
|
||||
if (footPrintService.HasFootPrint(EFootPrint.IsFaceBookRewardLink))
|
||||
{
|
||||
return;
|
||||
}
|
||||
footPrintService.UpdateFootPrint(EFootPrint.IsFaceBookRewardLink);
|
||||
|
||||
int rewardID = GContext.container.Resolve<Tables>().TbGlobalConfig.AccountLinkReward;
|
||||
ItemData itemData = GContext.container.Resolve<PlayerItemData>().GetItemDataOne(rewardID);
|
||||
GContext.container.Resolve<PlayerItemData>().AddItem(itemData, null);
|
||||
GContext.Publish(new ShowData(itemData));
|
||||
GContext.Publish(new ShowData());
|
||||
GContext.container.Resolve<PlayerShopData>().GetSKUs();
|
||||
}
|
||||
}
|
||||
class HeadItem : MonoBehaviour
|
||||
{
|
||||
public Image icon_head;
|
||||
Button btn_head;
|
||||
public GameObject selected;
|
||||
GameObject loading;
|
||||
public Action onClick;
|
||||
private void Awake()
|
||||
{
|
||||
btn_head = GetComponent<Button>();
|
||||
icon_head = transform.Find("mask/icon_head").GetComponent<Image>();
|
||||
selected = transform.Find("selected").gameObject;
|
||||
loading = transform.Find("loading").gameObject;
|
||||
btn_head.onClick.AddListener(OnClick);
|
||||
}
|
||||
public void SetSelected(bool value)
|
||||
{
|
||||
selected.SetActive(value);
|
||||
}
|
||||
void OnClick()
|
||||
{
|
||||
if (loading.activeSelf)
|
||||
{
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_102"));
|
||||
return;
|
||||
}
|
||||
if (onClick != null)
|
||||
{
|
||||
onClick();
|
||||
}
|
||||
}
|
||||
public async void SetUrlAsync(string url)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(url))
|
||||
{
|
||||
loading.SetActive(true);
|
||||
await GContext.container.Resolve<IUIService>().SetHeadImage(icon_head, url);
|
||||
if (loading != null)
|
||||
{
|
||||
loading.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using asap.core;
|
||||
using GameCore;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class SurveyPanel : MonoBehaviour
|
||||
{
|
||||
Button btn_close;
|
||||
RectTransform RectPos;
|
||||
WebView webView;
|
||||
int left; int top; int right; int bottom;
|
||||
DateTime startTime;
|
||||
SurveyService surveyService;
|
||||
cfg.SurveyData survey;
|
||||
bool closing;
|
||||
private void Awake()
|
||||
{
|
||||
webView = GetComponent<WebView>();
|
||||
btn_close = transform.Find("root/btn_close").GetComponent<Button>();
|
||||
RectPos = transform.Find("content").GetComponent<RectTransform>();
|
||||
}
|
||||
private void Start()
|
||||
{
|
||||
surveyService = GContext.container.Resolve<SurveyService>();
|
||||
survey = surveyService.tableData;
|
||||
startTime = ZZTimeHelper.UtcNow();
|
||||
UIManager.Instance.DestroyUI(UITypes.FishingMorePanel);
|
||||
#if UNITY_EDITOR
|
||||
string Url = surveyService.GetSurveyAnswer();
|
||||
Application.OpenURL(Url);
|
||||
#else
|
||||
Vector2 anchoredPosition1 = GetComponent<RectTransform>().anchoredPosition;
|
||||
Vector2 anchoredPosition = anchoredPosition1 + RectPos.anchoredPosition;
|
||||
var rect = RectPos.rect;
|
||||
var rectparent = UIManager.Instance.RectTrans.rect;
|
||||
int width = (int)rectparent.width / 2;
|
||||
int height = (int)rectparent.height / 2;
|
||||
|
||||
left = width + (int)(rect.xMin + anchoredPosition.x);
|
||||
right = width - (int)(rect.xMax + anchoredPosition.x);
|
||||
top = height - (int)(rect.yMax + anchoredPosition.y);
|
||||
bottom = height + (int)(rect.yMin + anchoredPosition.y);
|
||||
|
||||
string Url = surveyService.GetSurveyAnswer();
|
||||
webView.OnOpen(Url, left, top, right, bottom);
|
||||
#endif
|
||||
btn_close.onClick.AddListener(Close);
|
||||
}
|
||||
async void Close()
|
||||
{
|
||||
if (closing) return;
|
||||
closing = true;
|
||||
btn_close.interactable = false;
|
||||
try
|
||||
{
|
||||
if ((ZZTimeHelper.UtcNow() - startTime).TotalSeconds >= 10)
|
||||
await surveyService.GetSurveyCheckAnswer(survey);
|
||||
if (this != null && surveyService.IsCurrent) UIManager.Instance.DestroyUI(UITypes.SurveyPanel);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
// Close without replaying a reward whose save outcome is uncertain.
|
||||
if (this != null && surveyService.IsCurrent) UIManager.Instance.DestroyUI(UITypes.SurveyPanel);
|
||||
}
|
||||
}
|
||||
private void OnDisable()
|
||||
{
|
||||
webView.OnClose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using GameCore;
|
||||
|
||||
public class EventShootingRangeData : IChainPackData
|
||||
{
|
||||
[asap.core.Inject] public game.PlayerDataStorage.IPlayerSaveStore SaveStore { get; set; }
|
||||
|
||||
private RawData _data = new RawData();
|
||||
|
||||
private bool _kvLoadPresent;
|
||||
internal bool KVLoadPresent => _kvLoadPresent;
|
||||
internal static game.PlayerDataStorage.PlayerKVShootingRangeState DecodeForKVLoad(
|
||||
IReadOnlyDictionary<string, string> raw, game.PlayerDataStorage.PlayerKVSchemaSnapshot snapshot,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeShootingRange(raw, snapshot, errors);
|
||||
|
||||
internal void ResetForKVLoad()
|
||||
{
|
||||
_kvLoadPresent = false;
|
||||
_data = default(RawData);
|
||||
}
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVShootingRangeState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
if (!state.IsPresent) { ResetForKVLoad(); return; }
|
||||
var replacement = new RawData
|
||||
{
|
||||
EventId = state.EventId,
|
||||
CycleId = state.CycleId,
|
||||
RedirectId = state.RedirectId,
|
||||
AmmoCount = state.AmmoCount,
|
||||
CurrentStageIdx = state.CurrentStageIdx,
|
||||
ChainProgress = state.ChainProgress,
|
||||
RewardFlag = state.RewardFlag,
|
||||
TargetFlag = state.TargetFlag,
|
||||
HasBeenOpened = state.HasBeenOpened,
|
||||
Inflation = state.Inflation,
|
||||
};
|
||||
_data = replacement;
|
||||
_kvLoadPresent = true;
|
||||
}
|
||||
internal game.PlayerDataStorage.PlayerKVShootingRangeState SnapshotForKVValidation() =>
|
||||
new game.PlayerDataStorage.PlayerKVShootingRangeState(_kvLoadPresent, _data.EventId, _data.CycleId, _data.RedirectId, _data.AmmoCount, _data.CurrentStageIdx, _data.ChainProgress, _data.RewardFlag, _data.TargetFlag, _data.HasBeenOpened, _data.Inflation);
|
||||
private readonly Tables _tables = GContext.container.Resolve<Tables>();
|
||||
private readonly TbPack _pack = GContext.container.Resolve<Tables>().TbPack;
|
||||
private readonly TbEventPackManager _eventPackManager = GContext.container.Resolve<Tables>().TbEventPackManager;
|
||||
|
||||
private readonly TbEventShootingRangeMain _shootingRangeMainTable =
|
||||
GContext.container.Resolve<Tables>().TbEventShootingRangeMain;
|
||||
|
||||
public const string Key = "EventShootingRangeData",
|
||||
PackRedPointId = "EventShooting.Pack",
|
||||
EntranceRedPointId = "EventShooting.Enter";
|
||||
|
||||
private const int SlotCount = 6;
|
||||
public int EventId => _data.EventId;
|
||||
public int RedirectId => _data.RedirectId;
|
||||
public int AmmoCount => _data.AmmoCount;
|
||||
public float Inflation => _data.Inflation;
|
||||
public bool HasBeenOpened
|
||||
{
|
||||
get => _data.HasBeenOpened;
|
||||
set => _data.HasBeenOpened = value;
|
||||
}
|
||||
|
||||
public TimeSpan RemainingTime =>
|
||||
DateTime.Parse((_tables.TbFishingEvent[_data.EventId].TimeDefinition as LimitedTime)?.EndTime) -
|
||||
ZZTimeHelper.UtcNow();
|
||||
|
||||
public bool IsActive => _tables.TbFishingEvent.DataMap.ContainsKey(_data.EventId) &&
|
||||
_tables.TbFishingEventCycleItem.DataMap.ContainsKey(_data.CycleId) &&
|
||||
_tables.TbEventShootingRangeMain.DataMap.ContainsKey(_data.RedirectId) &&
|
||||
_tables.TbEventPackManager.DataMap.ContainsKey(_tables
|
||||
.TbEventShootingRangeMain[_data.RedirectId].PackId) &&
|
||||
RemainingTime.TotalSeconds > 0 &&
|
||||
ZZTimeHelper.UtcNow() >=
|
||||
DateTime.Parse((_tables.TbFishingEvent[_data.EventId].TimeDefinition as LimitedTime)
|
||||
?.StartTime);
|
||||
|
||||
public EventShootingRangeMain EventShootingRange =>
|
||||
GContext.container.Resolve<Tables>().TbEventShootingRangeMain[RedirectId];
|
||||
|
||||
public EventShootingRangeStage CurrentStage =>
|
||||
_tables.TbEventShootingRangeStage[EventShootingRange.StageList[CurrentStageIdx]];
|
||||
|
||||
public int CurrentStageIdx => _data.CurrentStageIdx % EventShootingRange.StageList.Count;
|
||||
public int RoundCount => _data.CurrentStageIdx / EventShootingRange.StageList.Count;
|
||||
|
||||
// public bool IsGameDepleted =>
|
||||
// _data.CurrentStageIdx < 0 || _data.CurrentStageIdx >= EventShootingRange.StageList.Count;
|
||||
|
||||
public bool IsGrandPrizeAllowed => CurrentStageShootCount >= CurrentStage.MinAttempts - 1;
|
||||
public bool IsFinalStage => CurrentStageIdx == EventShootingRange.StageList.Count - 1;
|
||||
|
||||
public int CurrentStageShootCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = 0;
|
||||
var flag = _data.RewardFlag;
|
||||
while (flag > 0)
|
||||
{
|
||||
count += (int)(flag & 1);
|
||||
flag >>= 1;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
private List<int> ChainList => _tables.TbEventPackManager[_tables.TbEventShootingRangeMain[_data.RedirectId].PackId]
|
||||
.VIPPackList[0];
|
||||
|
||||
public int ChainListCount => ChainList.Count;
|
||||
public bool IsChainPackDepleted => _data.ChainProgress >= ChainListCount;
|
||||
public bool IsEndGame => _data.ChainProgress > ChainList.Count - SlotCount;
|
||||
|
||||
public bool DoNeedPackRedPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsChainPackDepleted && ChainProgress < ChainListCount)
|
||||
return _pack[_eventPackManager[EventShootingRange.PackId].VIPPackList[0][ChainProgress]].IAPID == 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool DoNeedEntranceRedPoint => !HasBeenOpened || _data.AmmoCount >= CycleItem.RedDot;
|
||||
|
||||
public int ChainProgress
|
||||
{
|
||||
get => _data.ChainProgress;
|
||||
set => _data.ChainProgress = value;
|
||||
}
|
||||
|
||||
public Pack[] NormalPacks
|
||||
{
|
||||
get
|
||||
{
|
||||
// return [_tables.TbPack[EventShootingRange.PackId2], _tables.TbPack[EventShootingRange.PackId2 + 1]
|
||||
var normalPackIds = _tables.TbEventPackManager[EventShootingRange.PackId2].VIPPackList[0];
|
||||
var packs = new Pack[normalPackIds.Count];
|
||||
for (int i = 0; i < normalPackIds.Count; i++)
|
||||
{
|
||||
packs[i] = _tables.TbPack[normalPackIds[i]];
|
||||
}
|
||||
|
||||
return packs;
|
||||
}
|
||||
}
|
||||
|
||||
public string AmmoIconUrl => GContext.container.Resolve<Tables>().TbItem[CycleItem.ID].Icon;
|
||||
public string AmmoNameUrl => GContext.container.Resolve<Tables>().TbItem[CycleItem.ID].Name_l10n_key;
|
||||
|
||||
public List<int> GetTokenProgressRewardAfterAddingToken(int tokenAdded)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public FishingEventCycleItem CycleItem => _tables.TbFishingEventCycleItem[_data.CycleId];
|
||||
|
||||
#region UI Resources
|
||||
public string ChainPackPanelUrl => _shootingRangeMainTable[RedirectId].ChainPackPanel;
|
||||
public string PackPanelUrl => _shootingRangeMainTable[RedirectId].PackPanel;
|
||||
public string InfoPanelUrl => _shootingRangeMainTable[RedirectId].InfoPanel;
|
||||
public string MainPanelUrl => _shootingRangeMainTable[RedirectId].EventPanel;
|
||||
public string RewardPanelUrl => _shootingRangeMainTable[RedirectId].EventRewardPanel;
|
||||
public string ShootingRangeAct => _shootingRangeMainTable[RedirectId].Scene;
|
||||
public string EntranceIcon => _shootingRangeMainTable[RedirectId].IconEvent;
|
||||
public string PackIcon => _shootingRangeMainTable[RedirectId].IconPackage;
|
||||
public string TransitionPanelUrl => _shootingRangeMainTable[RedirectId].TransPanel;
|
||||
|
||||
#endregion
|
||||
public int AmmoId => CycleItem.ItemId;
|
||||
|
||||
// default purpose, not functional
|
||||
public string RedPointKey => "";
|
||||
public int RedPointIndex => 0;
|
||||
|
||||
public int GetChainProgressBySlotIdx(int slotIdx)
|
||||
{
|
||||
// Debug.Log($"<color=#f18c0a>Progress: {ChainProgress}</color>");
|
||||
int res;
|
||||
if (_data.ChainProgress > ChainList.Count - SlotCount)
|
||||
res = ChainList.Count - SlotCount + slotIdx;
|
||||
else
|
||||
res = _data.ChainProgress + slotIdx;
|
||||
Assert.IsTrue(res < ChainList.Count, $"Progress {res} out of range: {ChainListCount}");
|
||||
return res;
|
||||
}
|
||||
|
||||
public void UpdateData(FishingEvent e)
|
||||
{
|
||||
if (EventId == e.ID)
|
||||
return;
|
||||
_data = new RawData
|
||||
{
|
||||
EventId = e.ID,
|
||||
CycleId = e.RedirectID,
|
||||
RedirectId = _tables.TbFishingEventCycleItem[e.RedirectID].RedirectID,
|
||||
AmmoCount = _tables.TbFishingEventCycleItem[e.RedirectID].WelcomeGift,
|
||||
HasBeenOpened = false,
|
||||
Inflation = GContext.container.Resolve<PlayerData>().InflationRate
|
||||
};
|
||||
UploadData();
|
||||
}
|
||||
|
||||
public void LoadData(string s)
|
||||
{
|
||||
_data = Newtonsoft.Json.JsonConvert.DeserializeObject<RawData>(s);
|
||||
// _data.EventId = 0;
|
||||
// Debug.Log($"<color=#4d90fe>ShootingRangeDataLoaded: {s}</color>");
|
||||
}
|
||||
|
||||
public void UploadData()
|
||||
{
|
||||
var message = Newtonsoft.Json.JsonConvert.SerializeObject(_data);
|
||||
// Debug.Log($"<color=#4d90fe>ShootingRangeDataUploaded: {message}</color>");
|
||||
SaveStore.Write(Key, message).RequireAccepted();
|
||||
}
|
||||
|
||||
public void AddAmmo(int count)
|
||||
{
|
||||
_data.AmmoCount += count;
|
||||
GContext.container.Resolve<FishingEventData>().SaveTransitionData(_data.EventId, _data.AmmoCount);
|
||||
UploadData();
|
||||
}
|
||||
|
||||
public int PickRandomIndex(IList<int> weightList)
|
||||
{
|
||||
Assert.IsTrue(weightList is { Count: >= 1 }, "WeightList contains no weight.");
|
||||
var acc = new List<int> { weightList[0] };
|
||||
for (int i = 1; i < weightList.Count; i++)
|
||||
acc.Add(weightList[i] + acc[i - 1]);
|
||||
int random = UnityEngine.Random.Range(0, acc[^1]);
|
||||
for (int i = 0; i < acc.Count; i++)
|
||||
{
|
||||
if (random < acc[i])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checkout to next stage.
|
||||
/// </summary>
|
||||
/// <returns>False if next stage is out of range.</returns>
|
||||
public void SwitchToNextStage()
|
||||
{
|
||||
_data.CurrentStageIdx++;
|
||||
// _data.CurrentStageIdx %= EventShootingRange.StageList.Count;
|
||||
ResetRewardFlag();
|
||||
ResetTargetFlag();
|
||||
// return _data.CurrentStageIdx < EventShootingRange.StageList.Count;
|
||||
}
|
||||
|
||||
public void SwitchToStage(int n)
|
||||
{
|
||||
_data.CurrentStageIdx = n;
|
||||
ResetRewardFlag();
|
||||
ResetTargetFlag();
|
||||
}
|
||||
|
||||
public Pack GetChainPackByChainProgress(int chainProgress)
|
||||
{
|
||||
return _tables.TbPack[ChainList[chainProgress]];
|
||||
}
|
||||
|
||||
public void OnBuySuccess()
|
||||
{
|
||||
if (ChainProgress >= ChainListCount)
|
||||
{
|
||||
Debug.LogError($"Trying to buy pack no.{ChainProgress} while there are/is only {ChainListCount} packs.");
|
||||
return;
|
||||
}
|
||||
|
||||
_data.ChainProgress++;
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
public void ResetData()
|
||||
{
|
||||
_data.AmmoCount = 200;
|
||||
ResetRewardFlag();
|
||||
ResetTargetFlag();
|
||||
_data.CurrentStageIdx = 11;
|
||||
UploadData();
|
||||
Debug.Log("<color=#4d90fe>Data reset.</color>");
|
||||
}
|
||||
#endif
|
||||
|
||||
#region Flags
|
||||
|
||||
public void ResetRewardFlag()
|
||||
{
|
||||
_data.RewardFlag = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if reward No.idx in current stage is claimed.
|
||||
/// </summary>
|
||||
/// <param name="idx">Index of current reward. Starts with 0.</param>
|
||||
/// <returns>True if this reward is collected.</returns>
|
||||
public bool IsRewardCollected(int idx)
|
||||
{
|
||||
Assert.IsTrue(idx < CurrentStage.DropId.Count,
|
||||
$"Attempting to get reward{idx} status, while we have only {CurrentStage.DropId.Count} rewards.");
|
||||
long idxFlag = (long)1 << idx;
|
||||
return (_data.RewardFlag & idxFlag) == idxFlag;
|
||||
}
|
||||
|
||||
public void SetRewardCollected(int idx)
|
||||
{
|
||||
Assert.IsTrue(idx < CurrentStage.DropId.Count,
|
||||
$"Attempting to set reward{idx} as got, while we have only {CurrentStage.DropId.Count} rewards.");
|
||||
_data.RewardFlag |= (long)1 << idx;
|
||||
}
|
||||
|
||||
public void ResetTargetFlag()
|
||||
{
|
||||
_data.TargetFlag = 0;
|
||||
}
|
||||
|
||||
public bool IsTargetHit(int idx)
|
||||
{
|
||||
/*Assert.IsTrue(idx < CurrentStage.DropId.Count,
|
||||
$"Attempting to get target{idx} status, while we have only {CurrentStage.DropId.Count} targets.");
|
||||
Assert.IsTrue(false, "JP wins!");
|
||||
Assert.IsTrue(false,
|
||||
$"Attempting to get target{idx} status, while we have only {CurrentStage.DropId.Count} targets.");*/
|
||||
if (idx >= CurrentStage.DropId.Count)
|
||||
Debug.LogWarning($"尝试获取{idx}号靶子的状态,但是咱只有{CurrentStage.DropId.Count}个Drop。这很奇怪。");
|
||||
long idxFlag = (long)1 << idx;
|
||||
return (_data.TargetFlag & idxFlag) == idxFlag;
|
||||
}
|
||||
|
||||
public void SetTargetHit(int idx)
|
||||
{
|
||||
// Assert.IsTrue(idx < CurrentStage.DropId.Count,
|
||||
// $"Attempting to get target{idx} status, while we have only {CurrentStage.DropId.Count} targets.");
|
||||
if (idx >= CurrentStage.DropId.Count)
|
||||
Debug.LogWarning($"尝试打击{idx}号靶子,但是咱只有{CurrentStage.DropId.Count}个Drop。这很奇怪。");
|
||||
_data.TargetFlag |= ((long)1 << idx);
|
||||
}
|
||||
|
||||
public int GetHitTargetCount()
|
||||
{
|
||||
int count = 0;
|
||||
long num = _data.TargetFlag;
|
||||
while (num != 0)
|
||||
{
|
||||
num &= num - 1;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public List<ItemData> GetItemsByPackDropId(int dropId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private struct RawData
|
||||
{
|
||||
public int EventId;
|
||||
public int CycleId;
|
||||
public int RedirectId;
|
||||
public int AmmoCount;
|
||||
|
||||
/// <summary>
|
||||
/// Used in flag fashion.
|
||||
/// </summary>
|
||||
public long RewardFlag;
|
||||
|
||||
/// <summary>
|
||||
/// Used in flag fashion.
|
||||
/// </summary>
|
||||
public long TargetFlag;
|
||||
|
||||
/// <summary>
|
||||
/// Stage index that starts from 0. May exceed total stage count, indicating how many rounds player has counted.
|
||||
/// </summary>
|
||||
public int CurrentStageIdx;
|
||||
public int ChainProgress;
|
||||
public bool HasBeenOpened;
|
||||
public float Inflation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
|
||||
public class BlackFridayCouponData
|
||||
{
|
||||
private game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
internal void BindSaveStore(game.PlayerDataStorage.IPlayerSaveStore store)
|
||||
{
|
||||
if (saveStore != null && !ReferenceEquals(saveStore, store)) throw new System.InvalidOperationException("Archive store cannot change login.");
|
||||
saveStore = store;
|
||||
}
|
||||
|
||||
private readonly Tables _tables = GContext.container.Resolve<Tables>();
|
||||
private readonly TbEventCoupon _tableCoupon = GContext.container.Resolve<Tables>().TbEventCoupon;
|
||||
public DateTime ActivateTime = new DateTime(1996, 11, 28);
|
||||
public int EventId { get; private set; }
|
||||
public int RedirectId { get; private set; }
|
||||
public bool IsBought => _isBought;
|
||||
public bool DoesPop = true;
|
||||
private bool _isBought = false;
|
||||
public TimeSpan RemainingTime
|
||||
{
|
||||
get
|
||||
{
|
||||
var eventLimit = DateTime.Parse((_tables.TbFishingEvent[EventId].TimeDefinition as LimitedTime)?.EndTime);
|
||||
var activeLimit = ActivateTime + TimeSpan.FromSeconds(EventDuration);
|
||||
var limit = eventLimit < activeLimit ? eventLimit : activeLimit;
|
||||
return limit - ZZTimeHelper.UtcNow();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsWithinEventTime
|
||||
{
|
||||
get
|
||||
{
|
||||
var now = ZZTimeHelper.UtcNow();
|
||||
var fishingEvent = _tables.TbFishingEvent[EventId];
|
||||
return now >= DateTime.Parse((fishingEvent.TimeDefinition as LimitedTime)?.StartTime) &&
|
||||
now < DateTime.Parse((fishingEvent.TimeDefinition as LimitedTime)?.EndTime);
|
||||
}
|
||||
}
|
||||
public const string PlayFabDataKey = "BlackFridayCouponDate";
|
||||
|
||||
public bool IsActive => _tables.TbFishingEvent.DataMap.ContainsKey(EventId) &&
|
||||
_tableCoupon.DataMap.ContainsKey(RedirectId) && IsWithinEventTime &&
|
||||
ZZTimeHelper.UtcNow() <= ActivateTime + TimeSpan.FromSeconds(EventDuration) && !IsBought;
|
||||
public int Multiplier => GContext.container.Resolve<Tables>().TbEventCoupon[RedirectId].Multiple;
|
||||
public int EventDuration => GContext.container.Resolve<Tables>().TbEventCoupon[RedirectId].Time;
|
||||
internal void ResetForKVLoad()
|
||||
{ ActivateTime=default; EventId=0; RedirectId=0; _isBought=false; DoesPop=true; }
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVCouponState state)
|
||||
{ if(state==null) throw new ArgumentNullException(nameof(state)); ActivateTime=state.ActivateTime; EventId=state.EventId; RedirectId=state.RedirectId; _isBought=state.Bought; }
|
||||
|
||||
public void LoadData(string s)
|
||||
{
|
||||
int[] tokens = s.Split(',').Select(int.Parse).ToArray();
|
||||
ActivateTime = new DateTime(tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]);
|
||||
EventId = tokens[6];
|
||||
if (tokens.Length < 9)
|
||||
{
|
||||
_isBought = false;
|
||||
RedirectId = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
RedirectId = tokens[7];
|
||||
_isBought = tokens[8] == 1;
|
||||
}
|
||||
|
||||
// BlackFridayCouponPopupPanel.DebugWithColor("Debug Activated.");
|
||||
// EventId = 0;
|
||||
}
|
||||
|
||||
public void UploadData()
|
||||
{
|
||||
string s =
|
||||
$"{ActivateTime.Year},{ActivateTime.Month},{ActivateTime.Day},{ActivateTime.Hour},{ActivateTime.Minute},{ActivateTime.Second},{EventId},{RedirectId},{(IsBought ? 1 : 0)}";
|
||||
saveStore.Write(PlayFabDataKey, s).RequireAccepted();
|
||||
}
|
||||
public void UpdateData(FishingEvent e)
|
||||
{
|
||||
if (e.Type != 3 || e.SubType != 9 || e.ID == EventId)
|
||||
return ;
|
||||
EventId = e.ID;
|
||||
RedirectId = e.RedirectID;
|
||||
ActivateTime = ZZTimeHelper.UtcNow();
|
||||
_isBought = false;
|
||||
// Debug.Log($"<color=#23aaf2>Black Friday Activated @ {ActivateTime}</color>");
|
||||
UploadData();
|
||||
}
|
||||
|
||||
public void SetStateBought()
|
||||
{
|
||||
_isBought = true;
|
||||
UploadData();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
namespace GameCore
|
||||
{
|
||||
public partial class FishingEventData
|
||||
{
|
||||
public readonly ChainPackWithProgressMigrationData ChainPackWithProgressMigrationData = new ChainPackWithProgressMigrationData();
|
||||
}
|
||||
|
||||
}
|
||||
public class ChainPackWithProgressMigrationData : ThanksGivingPackData
|
||||
{
|
||||
internal new static game.PlayerDataStorage.PlayerKVProgressPackState DecodeForKVLoad(System.Collections.Generic.IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeProgressPack(raw,true,errors);
|
||||
public new const string Key = "ThanksGivingPackMigrationData";
|
||||
public override string RedPointKey => "thanks_giving";
|
||||
protected override string key => Key;
|
||||
public void UpdateData(int eventId, int packId)
|
||||
{
|
||||
// Debug.Log($"<color=#f18c0a>Updatedata {e.ID}, {e.RedirectID}, {TokenProgress}, {ChainProgress}</color>");
|
||||
if (eventId == 0 || packId == 0)
|
||||
return;
|
||||
var oldData = GContext.container.Resolve<FishingEventData>().ThanksGivingPackData;
|
||||
if (!GContext.container.Resolve<Tables>().TbFishingEvent.DataMap.ContainsKey(oldData.EventId)
|
||||
&& oldData.PackId == packId)
|
||||
{
|
||||
_data = new Data(eventId: eventId, redirectId: packId, tokenProgress: oldData.TokenProgress, chainProgress: oldData.ChainProgress);
|
||||
saveStore.Write(ThanksGivingPackData.Key, "").RequireAccepted();
|
||||
}
|
||||
else
|
||||
_data = new Data(eventId: eventId, redirectId: packId, tokenProgress: 0, chainProgress: 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using GameCore;
|
||||
using UnityEngine.Assertions;
|
||||
using Castle.Core.Internal;
|
||||
|
||||
namespace GameCore
|
||||
{
|
||||
public partial class FishingEventData
|
||||
{
|
||||
public readonly ThanksGivingPackData ThanksGivingPackData = new ThanksGivingPackData();
|
||||
}
|
||||
|
||||
}
|
||||
public class ThanksGivingPackData : IProgressChainPackData
|
||||
{
|
||||
protected game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
internal void BindSaveStore(game.PlayerDataStorage.IPlayerSaveStore store)
|
||||
{
|
||||
if (saveStore != null && !ReferenceEquals(saveStore, store)) throw new System.InvalidOperationException("Archive store cannot change login.");
|
||||
saveStore = store;
|
||||
}
|
||||
|
||||
protected Data _data = new Data(0, 0, 0, 0);
|
||||
|
||||
internal static game.PlayerDataStorage.PlayerKVProgressPackState DecodeForKVLoad(IReadOnlyDictionary<string,string> raw,
|
||||
game.PlayerDataStorage.PlayerKVSchemaDiagnostics errors) =>
|
||||
game.PlayerDataStorage.PlayerKVSaveSchema.DecodeProgressPack(raw,false,errors);
|
||||
internal void ResetForKVLoad() { _data = null; }
|
||||
internal void ReplaceFromKVDecoded(game.PlayerDataStorage.PlayerKVProgressPackState state)
|
||||
{
|
||||
if (state == null) throw new ArgumentNullException(nameof(state));
|
||||
_data = state.IsPresent ? new Data(state.EventId,state.RedirectId,state.TokenProgress,state.ChainProgress) : null;
|
||||
}
|
||||
internal game.PlayerDataStorage.PlayerKVProgressPackState SnapshotForKVValidation() => _data == null
|
||||
? new game.PlayerDataStorage.PlayerKVProgressPackState(false,0,0,0,0)
|
||||
: new game.PlayerDataStorage.PlayerKVProgressPackState(true,_data.EventId,_data.RedirectId,_data.TokenProgress,_data.ChainProgress);
|
||||
/// <summary>
|
||||
/// Starts with 0, representing the first unclaimed reward.
|
||||
/// </summary>
|
||||
public const int SlotCount = 6;
|
||||
public const string Key = "ThanksGivingPackData";
|
||||
protected virtual string key => Key;
|
||||
private readonly Tables _tables = GContext.container.Resolve<Tables>();
|
||||
private readonly TbFishingEvent _fishingEvent = GContext.container.Resolve<Tables>().TbFishingEvent;
|
||||
private readonly TbEventPackManager _eventPackManager = GContext.container.Resolve<Tables>().TbEventPackManager;
|
||||
private readonly TbPack _pack = GContext.container.Resolve<Tables>().TbPack;
|
||||
private readonly PlayerItemData _playerItemData = GContext.container.Resolve<PlayerItemData>();
|
||||
public const string ThanksGivingPanelName = "FestPack_ThanksGivingPanel";
|
||||
public const string WinterPanelName = "FestPack_WinterSnowPanel";
|
||||
public int PackId => _data == null ? 0 : _data.RedirectId;
|
||||
public bool DoNeedPackRedPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ChainProgress < ChainListCount)
|
||||
return _pack[_eventPackManager[_data.RedirectId].VIPPackList[0][ChainProgress]].IAPID == 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public TimeSpan RemainingTime =>
|
||||
DateTime.Parse((_tables.TbFishingEvent[_data.EventId].TimeDefinition as LimitedTime)?.EndTime) -
|
||||
ZZTimeHelper.UtcNow();
|
||||
|
||||
public bool IsWithinEventTime
|
||||
{
|
||||
get
|
||||
{
|
||||
var et = DateTime.Parse((_tables.TbFishingEvent[_data.EventId].TimeDefinition as LimitedTime).EndTime);
|
||||
var st = DateTime.Parse((_tables.TbFishingEvent[_data.EventId].TimeDefinition as LimitedTime).StartTime);
|
||||
return ZZTimeHelper.UtcNow() >= st && ZZTimeHelper.UtcNow() < et;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsActive => _data is not null
|
||||
&& _fishingEvent.DataMap.ContainsKey(_data.EventId)
|
||||
&& _eventPackManager.DataMap.ContainsKey(_data.RedirectId)
|
||||
&& IsWithinEventTime
|
||||
&& _data.ChainProgress < ChainListCount;
|
||||
public int TokenProgress => _data.TokenProgress;
|
||||
|
||||
public int ChainProgress
|
||||
{
|
||||
get => _data.ChainProgress;
|
||||
set => _data.ChainProgress = value;
|
||||
}
|
||||
public List<int> ChainList => _eventPackManager[_data.RedirectId].VIPPackList[0];
|
||||
public int ChainListCount => _eventPackManager[_data.RedirectId].VIPPackList[0].Count;
|
||||
public int EventId => _data.EventId;
|
||||
|
||||
private List<int> MileStoneList => GContext.container.Resolve<Tables>().TbEventPackManager[_data.RedirectId].MilestoneList;
|
||||
// public string TokenIconUrl => _tables.TbItem[_tables.TbDrop[_pack[ChainList[0]].DropID].DropList.DropIDList[0]].Icon;
|
||||
public string TokenIconUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
int tokenId = _tables.TbDrop[_pack[ChainList[0]].DropID].DropList.DropIDList[0];
|
||||
var emp = _eventPackManager[_data.RedirectId];
|
||||
// if (emp != null && emp.MilestoneItem.IsNullOrEmpty() && emp.MilestoneItem.Count >= 2 && tokenId == _eventPackManager[_data.RedirectId].MilestoneItem[0])
|
||||
if (emp?.MilestoneItem?.Count >= 2 && tokenId == _eventPackManager[_data.RedirectId].MilestoneItem[0])
|
||||
tokenId = _eventPackManager[_data.RedirectId].MilestoneItem[1];
|
||||
return _tables.TbItem[tokenId].Icon;
|
||||
}
|
||||
}
|
||||
public bool IsEndGame => ChainProgress > ChainListCount - SlotCount;
|
||||
public bool IsChainPackDepleted => TokenProgress >= MileStoneList.Sum();
|
||||
// public string PanelUrl => _pack[_eventPackManager[_data.RedirectId].VIPPackList[0][0]].Panel;
|
||||
// public string IconUrl => _pack[_eventPackManager[_data.RedirectId].VIPPackList[0][0]].Icon;
|
||||
|
||||
public string PanelUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
var res = _eventPackManager[_data.RedirectId].Panel;
|
||||
if (res == "")
|
||||
res = _pack[_eventPackManager[_data.RedirectId].VIPPackList[0][0]].Panel;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string IconUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
var res = _eventPackManager[_data.RedirectId].Icon;
|
||||
if (res == "")
|
||||
res = _pack[_eventPackManager[_data.RedirectId].VIPPackList[0][0]].Icon;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string RedPointKey => "home.thanks_giving";
|
||||
|
||||
public int RedPointIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
int res = 0;
|
||||
for (int i = ChainProgress; i < ChainListCount; i++)
|
||||
{
|
||||
var pack = _pack[_eventPackManager[_data.RedirectId].VIPPackList[0][i]];
|
||||
if (pack.IAPID == 0)
|
||||
res++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void UploadData()
|
||||
{
|
||||
saveStore.Write(key, _data.Serialize()).RequireAccepted();
|
||||
}
|
||||
public void LoadData(string s)
|
||||
{
|
||||
_data = Data.Deserialize(s);
|
||||
// Debug.LogError("Debug In Action @ Thanks Giving Load Data.");
|
||||
// _data.ChainProgress = 0;
|
||||
// _data.TokenProgress = 0;
|
||||
}
|
||||
public void UpdateData(FishingEvent e)
|
||||
{
|
||||
// Debug.Log($"<color=#f18c0a>Updatedata {e.ID}, {e.RedirectID}, {TokenProgress}, {ChainProgress}</color>");
|
||||
_data = new Data(eventId: e.ID, redirectId: _fishingEvent[e.ID].RedirectID, tokenProgress: 0, chainProgress: 0);
|
||||
}
|
||||
|
||||
public int GetTokenProgressTargetByIdx(int idx)
|
||||
{
|
||||
if (idx < MileStoneList.Count) return MileStoneList.GetRange(0, idx + 1).Sum();
|
||||
Debug.LogError($"Index {idx} is not within the scale of list length {MileStoneList.Count}");
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void GetTokenProgressTargetByProgress(int progress, out int targetDisplay, out int scoreDisplay)
|
||||
{
|
||||
// var mileStoneList = _eventPackManager[_data.RedirectId].MilestoneList;
|
||||
// Debug.Log($"<color=#f18c0a>VS: {progress}</color>");
|
||||
int i = 0, accTarget = 0;
|
||||
while (i < MileStoneList.Count)
|
||||
{
|
||||
accTarget += MileStoneList[i];
|
||||
if (accTarget > progress)
|
||||
{
|
||||
targetDisplay = MileStoneList[i];
|
||||
scoreDisplay = progress - MileStoneList.GetRange(0, i).Sum();
|
||||
// Debug.Log($"<color=#f18c0a>Within: {scoreDisplay} / {targetDisplay}</color>");
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
targetDisplay = MileStoneList[^1];
|
||||
scoreDisplay = targetDisplay;
|
||||
// Debug.Log($"<color=#f18c0a>Out: {scoreDisplay} / {targetDisplay}</color>");
|
||||
}
|
||||
|
||||
public int GetChainProgressBySlotIdx(int slotIdx)
|
||||
{
|
||||
// Debug.Log($"<color=#f18c0a>Progress: {ChainProgress}</color>");
|
||||
int res;
|
||||
if (ChainProgress > ChainListCount - SlotCount)
|
||||
res = ChainListCount - SlotCount + slotIdx;
|
||||
else
|
||||
res = ChainProgress + slotIdx;
|
||||
Assert.IsTrue(res < ChainListCount, $"Progress {res} out of range: {ChainListCount}");
|
||||
return res;
|
||||
}
|
||||
public Pack GetChainPackByChainProgress(int chainProgress)
|
||||
{
|
||||
return _pack[ChainList[chainProgress]];
|
||||
}
|
||||
|
||||
public List<ItemData> GetItemsByPackDropId(int dropId)
|
||||
{
|
||||
var res = new List<ItemData>();
|
||||
if (!_tables.TbDrop.DataMap.TryGetValue(dropId, out var drop))
|
||||
{
|
||||
Debug.Log($"Drop id {dropId} not found.");
|
||||
return res;
|
||||
}
|
||||
if (drop.Type != DropType.Probability)
|
||||
{
|
||||
Debug.Log($"Unsupported drop type {drop.Type} from drop Id {dropId}.");
|
||||
return res;
|
||||
}
|
||||
try
|
||||
{
|
||||
int n = drop.DropList.DropIDList.Count;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
var itemId = drop.DropList.DropIDList[i];
|
||||
var mileStoneItem = _eventPackManager[_data.RedirectId].MilestoneItem;
|
||||
if (!mileStoneItem.IsNullOrEmpty() && mileStoneItem.Count >= 2 && itemId == mileStoneItem[0])
|
||||
{
|
||||
itemId = mileStoneItem[1];
|
||||
}
|
||||
res.Add(new ItemData(itemId, drop.DropList.DropCountList[i]));
|
||||
//体力膨胀 实时
|
||||
_playerItemData.ItemTransition(res[i]);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[EventBreak]Invalid drop id {drop}: {e.Message}\n{e.StackTrace}");
|
||||
return res;
|
||||
}
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
public int GetRewardDropByChainProgress(int chainProgress)
|
||||
{
|
||||
int i, sum = 0;
|
||||
// var mileStoneList = _eventPackManager[_data.RedirectId].MilestoneList;
|
||||
for (i = 0; i < MileStoneList.Count - 1; i++)
|
||||
{
|
||||
sum += MileStoneList[i];
|
||||
if (chainProgress < sum)
|
||||
break;
|
||||
}
|
||||
var mileStoneRewards = _eventPackManager[_data.RedirectId].MilestoneReward;
|
||||
return mileStoneRewards[i];
|
||||
}
|
||||
|
||||
public void OnBuySuccess()
|
||||
{
|
||||
if (ChainProgress >= ChainListCount)
|
||||
{
|
||||
Debug.LogError($"Trying to buy pack no.{ChainProgress} while there are only {ChainListCount} packs.");
|
||||
return;
|
||||
}
|
||||
int tokenAdded = _tables.TbDrop[GetChainPackByChainProgress(_data.ChainProgress).DropID].DropList.DropCountList[0];
|
||||
_data.ChainProgress++;
|
||||
// Debug.Log($"<color=#f18c0a>AddProgress: {ChainProgress}</color>");
|
||||
// And magically, token is added already by Leyuan. So...
|
||||
var dropList = GetTokenProgressRewardAfterAddingToken(tokenAdded);
|
||||
// Debug.Log($"<color=#f18c0a>[EventPartner]Paid. Progress reward in onBuySuccess.</color>");
|
||||
// foreach (var r in dropList)
|
||||
// Debug.Log($"<color=#f18c0a>{r}</color>");
|
||||
_playerItemData.AddItemByDropList(dropList, null, false);
|
||||
// GContext.Publish(new ShowData(_playerItemData.GetItemDataByDropId(dropList)));
|
||||
// Debug.Log($"<color=#f18c0a>On Buy Success</color>");
|
||||
// GContext.Publish(new ShowData());
|
||||
}
|
||||
|
||||
public List<int> GetTokenProgressRewardAfterAddingToken(int tokenAdded)
|
||||
{
|
||||
// Debug.Log($"<color=#f18c0a>Add {tokenAdded}</color>");
|
||||
int previousTokenProgress = _data.TokenProgress - tokenAdded, i, sum = 0, previousIdx = -1, currentIdx = -1;
|
||||
for (i = 0; i < MileStoneList.Count; i++)
|
||||
{
|
||||
sum += MileStoneList[i];
|
||||
if (previousIdx == -1 && previousTokenProgress < sum)
|
||||
previousIdx = i;
|
||||
if (currentIdx == -1 && _data.TokenProgress < sum)
|
||||
currentIdx = i;
|
||||
}
|
||||
|
||||
if (currentIdx == -1 && _data.TokenProgress >= sum)
|
||||
currentIdx = MileStoneList.Count;
|
||||
if (previousIdx == -1 && previousTokenProgress >= sum)
|
||||
previousIdx = MileStoneList.Count;
|
||||
// Debug.Log($"<color=#f18c0a>current:{currentIdx}, previous {previousIdx}</color>");
|
||||
int count = Math.Max(0, currentIdx - previousIdx);
|
||||
if (count > 0)
|
||||
{
|
||||
var res = _eventPackManager[_data.RedirectId].MilestoneReward.GetRange(previousIdx, count);
|
||||
return res;
|
||||
}
|
||||
return new List<int>();
|
||||
// Debug.Log($"<color=#f18c0a>Progress Reward Length: {res.Count}, or {count}</color>");
|
||||
}
|
||||
|
||||
public void AddToken(int n)
|
||||
{
|
||||
_data.TokenProgress += n;
|
||||
UploadData();
|
||||
}
|
||||
|
||||
public int PickRandomIndex(IList<int> weightList)
|
||||
{
|
||||
// Assert.IsTrue(weightList.Count >= 1, )
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void ChainProgressIncrease()
|
||||
{
|
||||
ChainProgress++;
|
||||
}
|
||||
|
||||
public async void ShowChainPack()
|
||||
{
|
||||
if (IsChainPackDepleted)
|
||||
{
|
||||
//无限购买
|
||||
}
|
||||
else
|
||||
{
|
||||
UIType uIType = new UIType(PanelUrl);
|
||||
GameObject go = await UIManager.Instance.ShowUI(uIType);
|
||||
if (go != null)
|
||||
{
|
||||
go.GetComponent<ThanksGivingPopupPanel>().Init(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Data
|
||||
{
|
||||
public int TokenProgress, ChainProgress, EventId, RedirectId;
|
||||
public static Data Deserialize(string s)
|
||||
{
|
||||
if (s == null || s == "")
|
||||
{
|
||||
return new Data(eventId: 0, redirectId: 0, tokenProgress: 0, chainProgress: 0);
|
||||
}
|
||||
string[] tokens = s.Split(",");
|
||||
if (tokens.Length < 4)
|
||||
{
|
||||
Debug.LogError($"Wrong amount of parameters. Expect 4, but got {tokens.Length} in \"{s}\".");
|
||||
return new Data(eventId: 0, redirectId: 0, tokenProgress: 0, chainProgress: 0);
|
||||
}
|
||||
return new Data(eventId: int.Parse(tokens[0]), redirectId: int.Parse(tokens[1]),
|
||||
tokenProgress: int.Parse(tokens[2]), chainProgress: int.Parse(tokens[3]));
|
||||
}
|
||||
public string Serialize()
|
||||
{
|
||||
return $"{EventId},{RedirectId},{TokenProgress},{ChainProgress}";
|
||||
}
|
||||
|
||||
public Data(int eventId, int redirectId, int tokenProgress, int chainProgress)
|
||||
{
|
||||
TokenProgress = tokenProgress;
|
||||
ChainProgress = chainProgress;
|
||||
EventId = eventId;
|
||||
RedirectId = redirectId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,711 @@
|
||||
using asap.core;
|
||||
using cfg;
|
||||
using EnhancedUI.EnhancedScroller;
|
||||
using GameCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityNative.Sharing;
|
||||
|
||||
namespace game
|
||||
{
|
||||
public class FishingFriendAddPanel : MonoBehaviour, IEnhancedScrollerDelegate
|
||||
{
|
||||
game.PlayerDataStorage.IPlayerSaveStore saveStore;
|
||||
|
||||
|
||||
#region 分享
|
||||
Button btn_invite;
|
||||
GameObject icon_gift_invite;
|
||||
#endregion
|
||||
GameObject empty;
|
||||
GameObject loading;
|
||||
Button btn_add;
|
||||
// 好友码相关
|
||||
TMP_Text text_code;
|
||||
Button btn_copy;
|
||||
Button btn_share;
|
||||
// ScrollView相关
|
||||
EnhancedScroller scroller;
|
||||
public FriendInvitedItemTitleBar invitedItemTitleBar;
|
||||
public FriendInvitedItem invitedItem;
|
||||
public FriendRecommendedItemTitleBar recommendedItemTitleBar;
|
||||
public FriendRecommendedItem recommendedItem;
|
||||
|
||||
List<FriendAddItemData> m_DataList = new List<FriendAddItemData>();
|
||||
List<FriendAddItemData> m_InvitedDataList = new List<FriendAddItemData>();
|
||||
FriendRecommendedListData m_RecommendedListData;
|
||||
|
||||
int m_PageNo = 0;
|
||||
|
||||
#region 后台网络处理
|
||||
private List<FriendAddPanelBackendOperation> m_BackendOperationList = new List<FriendAddPanelBackendOperation>();
|
||||
|
||||
#endregion
|
||||
|
||||
private IUnityNativeSharing adapter;
|
||||
|
||||
#region 主页处理 / 空白搜索处理
|
||||
private async Task InitDefault()
|
||||
{
|
||||
m_DataList.Clear();
|
||||
m_InvitedDataList.Clear();
|
||||
if (m_RecommendedListData != null && m_RecommendedListData.RecommendedList != null)
|
||||
{
|
||||
m_RecommendedListData.RecommendedList.Clear();
|
||||
}
|
||||
if (IsExpire())
|
||||
{
|
||||
// 获取HomePage数据
|
||||
string json = await GContext.container.Resolve<FriendService>().GetHomePage(GContext.container.Resolve<IUserService>().UserId, m_PageNo);
|
||||
bool flag = true;
|
||||
if (string.IsNullOrEmpty(json))
|
||||
{
|
||||
flag = false;
|
||||
}
|
||||
GetHomePageResp getHomePageResp = null;
|
||||
if (flag == true)
|
||||
{
|
||||
try
|
||||
{
|
||||
getHomePageResp = Newtonsoft.Json.JsonConvert.DeserializeObject<GetHomePageResp>(json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GameDebug.LogError("FishingFriendAddPanel deserialize [GetHomePageResp] data has failed. Exception: " + ex.ToString());
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
m_DataList.Clear();
|
||||
if (flag == false)
|
||||
{
|
||||
scroller.gameObject.SetActive(false);
|
||||
empty.SetActive(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (getHomePageResp.Code > 0)
|
||||
{
|
||||
List<BFriendInfo> list = new List<BFriendInfo>();
|
||||
if (getHomePageResp.Friends != null)
|
||||
{
|
||||
foreach (var item in getHomePageResp.Friends)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(item))
|
||||
{
|
||||
BFriendInfo info = Newtonsoft.Json.JsonConvert.DeserializeObject<BFriendInfo>(item);
|
||||
FriendHelper.FixBFriendInfoData(info);
|
||||
list.Add(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
// InvitedItem TitleBar Data
|
||||
var invitedTitleBarData = new FriendAddItemData()
|
||||
{
|
||||
Type = FriendHelper.Friend_Add_Item_Type_Invited_TitleBar,
|
||||
FriendInfo = null,
|
||||
Extra = 0
|
||||
};
|
||||
m_InvitedDataList.Add(invitedTitleBarData);
|
||||
m_DataList.Add(invitedTitleBarData);
|
||||
|
||||
// InvitedItem Data
|
||||
foreach (var item in list)
|
||||
{
|
||||
var invitedItemData = new FriendAddItemData()
|
||||
{
|
||||
Type = FriendHelper.Friend_Add_Item_Type_Invited_Item,
|
||||
FriendInfo = item,
|
||||
Extra = 0
|
||||
};
|
||||
m_DataList.Add(invitedItemData);
|
||||
m_InvitedDataList.Add(invitedItemData);
|
||||
}
|
||||
}
|
||||
|
||||
if (getHomePageResp.RecommendedList != null && getHomePageResp.RecommendedList.Length > 0)
|
||||
{
|
||||
if (m_RecommendedListData != null && m_RecommendedListData.RecommendedList != null)
|
||||
{
|
||||
m_RecommendedListData.RecommendedList.Clear();
|
||||
}
|
||||
m_RecommendedListData = new FriendRecommendedListData();
|
||||
m_RecommendedListData.RecommendedList = new List<FriendAddItemData>();
|
||||
m_RecommendedListData.ExpirationDate = DateTime.Now.AddDays(1);
|
||||
|
||||
// RecommendedItem TitleBar Data
|
||||
var recommendedTitleBarData = new FriendAddItemData()
|
||||
{
|
||||
Type = FriendHelper.Friend_Add_Item_Type_Recommended_TitleBar,
|
||||
FriendInfo = null,
|
||||
Extra = 0
|
||||
};
|
||||
m_RecommendedListData.RecommendedList.Add(recommendedTitleBarData);
|
||||
m_DataList.Add(recommendedTitleBarData);
|
||||
|
||||
// RecommendedItem Data
|
||||
foreach (var item in getHomePageResp.RecommendedList)
|
||||
{
|
||||
var recommendedItemData = new FriendAddItemData()
|
||||
{
|
||||
Type = FriendHelper.Friend_Add_Item_Type_Recommended_Item,
|
||||
FriendInfo = item,
|
||||
Extra = 0
|
||||
};
|
||||
m_RecommendedListData.RecommendedList.Add(recommendedItemData);
|
||||
m_DataList.Add(recommendedItemData);
|
||||
}
|
||||
SyncRecommendedListData();
|
||||
}
|
||||
}
|
||||
if (m_DataList.Count > 0)
|
||||
{
|
||||
scroller.gameObject.SetActive(true);
|
||||
empty.SetActive(false);
|
||||
scroller.ReloadData();
|
||||
}
|
||||
else
|
||||
{
|
||||
scroller.gameObject.SetActive(false);
|
||||
empty.SetActive(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 获取邀请列表数据,再合并推荐列表数据
|
||||
string json = await GContext.container.Resolve<FriendService>().GetInvitedList();
|
||||
bool flag = true;
|
||||
if (string.IsNullOrEmpty(json))
|
||||
{
|
||||
flag = false;
|
||||
|
||||
}
|
||||
GetInvitedListResp getInvitedListResp = null;
|
||||
if (flag == true)
|
||||
{
|
||||
try
|
||||
{
|
||||
getInvitedListResp = Newtonsoft.Json.JsonConvert.DeserializeObject<GetInvitedListResp>(json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GameDebug.LogError("FishingFriendAddPanel deserialize [GetInvitedListResp] data has failed. Exception: " + ex.ToString());
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
m_DataList.Clear();
|
||||
if (flag == false)
|
||||
{
|
||||
scroller.gameObject.SetActive(false);
|
||||
empty.SetActive(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (getInvitedListResp.Code > 0)
|
||||
{
|
||||
List<BFriendInfo> list = new List<BFriendInfo>();
|
||||
if (getInvitedListResp.Friends != null)
|
||||
{
|
||||
foreach (var item in getInvitedListResp.Friends)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(item))
|
||||
{
|
||||
BFriendInfo info = Newtonsoft.Json.JsonConvert.DeserializeObject<BFriendInfo>(item);
|
||||
FriendHelper.FixBFriendInfoData(info);
|
||||
list.Add(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
// InvitedItem TitleBar Data
|
||||
var invitedTitleBarData = new FriendAddItemData()
|
||||
{
|
||||
Type = FriendHelper.Friend_Add_Item_Type_Invited_TitleBar,
|
||||
FriendInfo = null,
|
||||
Extra = 0
|
||||
};
|
||||
m_InvitedDataList.Add(invitedTitleBarData);
|
||||
m_DataList.Add(invitedTitleBarData);
|
||||
|
||||
// InvitedItem Data
|
||||
foreach (var item in list)
|
||||
{
|
||||
var invitedItemData = new FriendAddItemData()
|
||||
{
|
||||
Type = FriendHelper.Friend_Add_Item_Type_Invited_Item,
|
||||
FriendInfo = item,
|
||||
Extra = 0
|
||||
};
|
||||
m_InvitedDataList.Add(invitedItemData);
|
||||
m_DataList.Add(invitedItemData);
|
||||
}
|
||||
}
|
||||
if (m_RecommendedListData != null && m_RecommendedListData.RecommendedList != null)
|
||||
{
|
||||
m_DataList.AddRange(m_RecommendedListData.RecommendedList.ToArray());
|
||||
}
|
||||
}
|
||||
if (m_DataList.Count > 0)
|
||||
{
|
||||
scroller.gameObject.SetActive(true);
|
||||
scroller.ReloadData();
|
||||
empty.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
scroller.gameObject.SetActive(false);
|
||||
empty.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsExpire()
|
||||
{
|
||||
if (PlayerPrefs.HasKey(FriendHelper.FriendRecommendedListDataKey) == false) return true;
|
||||
string data = PlayerPrefs.GetString(FriendHelper.FriendRecommendedListDataKey);
|
||||
if (string.IsNullOrEmpty(data)) return true;
|
||||
|
||||
m_RecommendedListData = Newtonsoft.Json.JsonConvert.DeserializeObject<FriendRecommendedListData>(data);
|
||||
if (m_RecommendedListData == null) return true;
|
||||
|
||||
if (DateTime.Now < m_RecommendedListData.ExpirationDate) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SyncRecommendedListData()
|
||||
{
|
||||
lock (m_RecommendedListData)
|
||||
{
|
||||
if (m_RecommendedListData == null)
|
||||
{
|
||||
if (PlayerPrefs.HasKey(FriendHelper.FriendRecommendedListDataKey))
|
||||
{
|
||||
PlayerPrefs.DeleteKey(FriendHelper.FriendRecommendedListDataKey);
|
||||
}
|
||||
return;
|
||||
}
|
||||
PlayerPrefs.SetString(FriendHelper.FriendRecommendedListDataKey, Newtonsoft.Json.JsonConvert.SerializeObject(m_RecommendedListData));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 数据相关
|
||||
List<FriendAddPanelBackendOperation> m_RecommendedBackendOperationList = new List<FriendAddPanelBackendOperation>();
|
||||
public void StartRecommendedInvite(FriendAddItemData data)
|
||||
{
|
||||
var backendOperation = new FriendAddPanelBackendOperation();
|
||||
|
||||
m_RecommendedBackendOperationList.Add(backendOperation);
|
||||
|
||||
backendOperation.StartRecommendedInvite(this, data, ResumeRecommendedData);
|
||||
|
||||
RemoveRecommendedData(data);
|
||||
}
|
||||
public void ResumeRecommendedData(FriendAddItemData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
if (m_RecommendedBackendOperationList == null || m_RecommendedBackendOperationList.Count < 1) return;
|
||||
|
||||
bool flag = false;
|
||||
int count = m_RecommendedBackendOperationList.Count;
|
||||
for (int i = count - 1; i >= 0; --i)
|
||||
{
|
||||
var item = m_RecommendedBackendOperationList[i];
|
||||
if (item != null && item.Data != null && item.Data.EqualsPlayFabId(data))
|
||||
{
|
||||
flag = true;
|
||||
m_RecommendedBackendOperationList.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag) return;
|
||||
|
||||
if (m_RecommendedListData == null || m_RecommendedListData.RecommendedList == null) return;
|
||||
|
||||
m_RecommendedListData.RecommendedList.Add(data);
|
||||
|
||||
CombineData();
|
||||
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Friend_Add, m_InvitedDataList.Count > 0);
|
||||
}
|
||||
public void RemoveRecommendedData(FriendAddItemData data)
|
||||
{
|
||||
if (m_RecommendedListData == null || m_RecommendedListData.RecommendedList == null) return;
|
||||
|
||||
lock (m_RecommendedListData.RecommendedList)
|
||||
{
|
||||
for (int i = m_InvitedDataList.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var tmpData = m_InvitedDataList[i];
|
||||
if (tmpData.EqualsPlayFabId(data))
|
||||
{
|
||||
m_InvitedDataList.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
for (int i = m_RecommendedListData.RecommendedList.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var tmpData = m_RecommendedListData.RecommendedList[i];
|
||||
if (tmpData.EqualsPlayFabId(data))
|
||||
{
|
||||
m_RecommendedListData.RecommendedList.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_InvitedDataList.Count <= 1)
|
||||
{
|
||||
for (int i = m_InvitedDataList.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var tmpData = m_InvitedDataList[i];
|
||||
if (tmpData.Type == FriendHelper.Friend_Add_Item_Type_Invited_TitleBar)
|
||||
{
|
||||
m_InvitedDataList.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_RecommendedListData.RecommendedList.Count <= 1)
|
||||
{
|
||||
for (int i = m_RecommendedListData.RecommendedList.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var tmpData = m_RecommendedListData.RecommendedList[i];
|
||||
if (tmpData.Type == FriendHelper.Friend_Add_Item_Type_Recommended_TitleBar)
|
||||
{
|
||||
m_RecommendedListData.RecommendedList.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SyncRecommendedListData();
|
||||
|
||||
CombineData();
|
||||
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Friend_Add, m_InvitedDataList.Count > 0);
|
||||
}
|
||||
|
||||
List<FriendAddPanelBackendOperation> m_InvitedBackendOperationList = new List<FriendAddPanelBackendOperation>();
|
||||
public void StartInvitedAccept(FriendAddItemData data)
|
||||
{
|
||||
var backendOperation = new FriendAddPanelBackendOperation();
|
||||
|
||||
m_InvitedBackendOperationList.Add(backendOperation);
|
||||
|
||||
backendOperation.StartInvitedAccept(this, data, ResumeInvitedData);
|
||||
|
||||
RemoveInvitedData(data);
|
||||
}
|
||||
public void StartInvitedIgnore(FriendAddItemData data)
|
||||
{
|
||||
var backendOperation = new FriendAddPanelBackendOperation();
|
||||
|
||||
m_RecommendedBackendOperationList.Add(backendOperation);
|
||||
|
||||
backendOperation.StartInvitedIgnore(this, data, ResumeInvitedData);
|
||||
|
||||
RemoveInvitedData(data);
|
||||
}
|
||||
public void ResumeInvitedData(FriendAddItemData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
if (m_InvitedBackendOperationList == null || m_InvitedBackendOperationList.Count < 1) return;
|
||||
|
||||
bool flag = false;
|
||||
int count = m_InvitedBackendOperationList.Count;
|
||||
for (int i = count - 1; i >= 0; --i)
|
||||
{
|
||||
var item = m_InvitedBackendOperationList[i];
|
||||
if (item != null && item.Data != null && item.Data.EqualsPlayFabId(data))
|
||||
{
|
||||
flag = true;
|
||||
m_InvitedBackendOperationList.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag) return;
|
||||
|
||||
if (m_InvitedDataList == null) return;
|
||||
|
||||
m_InvitedDataList.Add(data);
|
||||
|
||||
CombineData();
|
||||
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Friend_Add, m_InvitedDataList.Count > 0);
|
||||
}
|
||||
public void RemoveInvitedData(FriendAddItemData data)
|
||||
{
|
||||
if (m_InvitedDataList == null) return;
|
||||
|
||||
lock (m_InvitedDataList)
|
||||
{
|
||||
for (int i = m_InvitedDataList.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var tmpData = m_InvitedDataList[i];
|
||||
if (tmpData.EqualsPlayFabId(data))
|
||||
{
|
||||
m_InvitedDataList.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
for (int i = m_RecommendedListData.RecommendedList.Count - 1; i >= 0; --i)
|
||||
{
|
||||
var tmpData = m_RecommendedListData.RecommendedList[i];
|
||||
if (tmpData.EqualsPlayFabId(data))
|
||||
{
|
||||
m_RecommendedListData.RecommendedList.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_InvitedDataList.Count <= 1)
|
||||
{
|
||||
m_InvitedDataList.Clear();
|
||||
}
|
||||
if (m_RecommendedListData.RecommendedList.Count <= 1)
|
||||
{
|
||||
m_RecommendedListData.RecommendedList.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
CombineData();
|
||||
|
||||
GContext.container.Resolve<FriendService>().GetInvitedListCount();
|
||||
RedPointManager.Instance.SetRedPointState(RedPointName.Home_Friend_Add, m_InvitedDataList.Count > 0);
|
||||
}
|
||||
public void CombineData()
|
||||
{
|
||||
lock (m_DataList)
|
||||
{
|
||||
m_DataList.Clear();
|
||||
if (m_InvitedDataList != null && m_InvitedDataList.Count > 1)
|
||||
{
|
||||
m_DataList.AddRange(m_InvitedDataList.ToArray());
|
||||
}
|
||||
if (m_RecommendedListData != null && m_RecommendedListData.RecommendedList != null && m_RecommendedListData.RecommendedList.Count > 1)
|
||||
{
|
||||
m_DataList.AddRange(m_RecommendedListData.RecommendedList.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (m_DataList.Count > 0)
|
||||
{
|
||||
scroller.gameObject.SetActive(true);
|
||||
empty.SetActive(false);
|
||||
var position = scroller.ScrollPosition;
|
||||
scroller.ReloadData();
|
||||
scroller.ScrollPosition = position;
|
||||
}
|
||||
else
|
||||
{
|
||||
scroller.gameObject.SetActive(false);
|
||||
empty.SetActive(true);
|
||||
}
|
||||
}
|
||||
private void UpdateLockAction(float deltaTime)
|
||||
{
|
||||
if (m_LockScrollViewCumulativeTime > 0)
|
||||
{
|
||||
m_LockScrollViewCumulativeTime -= deltaTime;
|
||||
}
|
||||
if (m_LockScrollViewCumulativeTime < 0)
|
||||
{
|
||||
m_LockScrollViewCumulativeTime = 0f;
|
||||
UnlockScrollView();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region UI相关
|
||||
|
||||
private void OnBtnCopyClick()
|
||||
{
|
||||
GUIUtility.systemCopyBuffer = text_code.text;
|
||||
ToastPanel.Show(LocalizationMgr.GetText("UI_ToastPanel_25"));
|
||||
}
|
||||
|
||||
void OnClickInviteLink()
|
||||
{
|
||||
if (adapter == null)
|
||||
{
|
||||
adapter = UnityNativeSharing.Create();
|
||||
}
|
||||
string invite = LocalizationMgr.GetFormatTextValue("UI_FishingSocialPanel_45", text_code.text);
|
||||
adapter.ShareText(invite);
|
||||
}
|
||||
private string GetInvitationCodeByPlayFabID()
|
||||
{
|
||||
return GContext.container.Resolve<FriendService>().GetInvitationCodeByPlayFabID(GContext.container.Resolve<IUserService>().UserId);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region EnhancedScroller Handlers
|
||||
private float m_LockScrollViewCumulativeTime = 0.0f;
|
||||
public void StartLockScrollView(float lockTime)
|
||||
{
|
||||
m_LockScrollViewCumulativeTime = lockTime;
|
||||
LockScrollView();
|
||||
}
|
||||
void LockScrollView()
|
||||
{
|
||||
if (scroller == null || scroller.ScrollRect == null) return;
|
||||
if (scroller.ScrollRect.vertical)
|
||||
{
|
||||
scroller.ScrollRect.vertical = false;
|
||||
}
|
||||
}
|
||||
|
||||
void UnlockScrollView()
|
||||
{
|
||||
if (scroller == null || scroller.ScrollRect == null) return;
|
||||
if (!scroller.ScrollRect.vertical)
|
||||
{
|
||||
scroller.ScrollRect.vertical = true;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetNumberOfCells(EnhancedScroller scroller)
|
||||
{
|
||||
return m_DataList.Count;
|
||||
}
|
||||
|
||||
public float GetCellViewSize(EnhancedScroller scroller, int dataIndex)
|
||||
{
|
||||
if (m_DataList[dataIndex].Type == FriendHelper.Friend_Add_Item_Type_Invited_TitleBar)
|
||||
{
|
||||
return 72f;
|
||||
}
|
||||
else if (m_DataList[dataIndex].Type == FriendHelper.Friend_Add_Item_Type_Invited_Item)
|
||||
{
|
||||
return 150f;
|
||||
}
|
||||
else if (m_DataList[dataIndex].Type == FriendHelper.Friend_Add_Item_Type_Recommended_TitleBar)
|
||||
{
|
||||
return 72f;
|
||||
}
|
||||
return 150f;
|
||||
}
|
||||
|
||||
public EnhancedScrollerCellView GetCellView(EnhancedScroller scroller, int dataIndex, int cellIndex)
|
||||
{
|
||||
FriendAddItem cellView;
|
||||
|
||||
if (m_DataList[dataIndex].Type == FriendHelper.Friend_Add_Item_Type_Invited_TitleBar)
|
||||
{
|
||||
cellView = scroller.GetCellView(invitedItemTitleBar) as FriendInvitedItemTitleBar;
|
||||
cellView.name = "[Invited Title Bar]";
|
||||
cellView.SetData(this, m_DataList[dataIndex], RemoveInvitedData);
|
||||
}
|
||||
else if (m_DataList[dataIndex].Type == FriendHelper.Friend_Add_Item_Type_Invited_Item)
|
||||
{
|
||||
cellView = scroller.GetCellView(invitedItem) as FriendInvitedItem;
|
||||
cellView.name = "[Invited Item] " + cellIndex + "-" + dataIndex;
|
||||
cellView.SetData(this, m_DataList[dataIndex], RemoveInvitedData);
|
||||
}
|
||||
else if (m_DataList[dataIndex].Type == FriendHelper.Friend_Add_Item_Type_Recommended_TitleBar)
|
||||
{
|
||||
cellView = scroller.GetCellView(recommendedItemTitleBar) as FriendRecommendedItemTitleBar;
|
||||
cellView.name = "[Recommended Title Bar]";
|
||||
cellView.SetData(this, m_DataList[dataIndex], RemoveRecommendedData);
|
||||
}
|
||||
else
|
||||
{
|
||||
cellView = scroller.GetCellView(recommendedItem) as FriendRecommendedItem;
|
||||
cellView.name = "[Recommended Item] " + cellIndex + "-" + dataIndex;
|
||||
cellView.SetData(this, m_DataList[dataIndex], RemoveRecommendedData);
|
||||
}
|
||||
|
||||
return cellView;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
void Awake()
|
||||
{
|
||||
saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
loading = transform.Find("loading").gameObject;
|
||||
empty = transform.Find("add").gameObject;
|
||||
empty.SetActive(false);
|
||||
btn_add = transform.Find("friendcode/btn_search").GetComponent<Button>();
|
||||
icon_gift_invite = transform.Find("btn_invite/btn_green/Ani_Container/p_text/icon").gameObject;
|
||||
btn_invite = transform.Find("btn_invite/btn_green").GetComponent<Button>();
|
||||
btn_invite.onClick.AddListener(OnInviteClick);
|
||||
btn_add.onClick.AddListener(OnAddClick);
|
||||
btn_share = transform.Find("friendcode/btn_share").GetComponent<Button>();
|
||||
btn_share.onClick.AddListener(OnClickInviteLink);
|
||||
text_code = transform.Find("friendcode/text_code").GetComponent<TMP_Text>();
|
||||
btn_copy = transform.Find("friendcode/btn_copy").GetComponent<Button>();
|
||||
btn_copy.onClick.AddListener(OnBtnCopyClick);
|
||||
text_code.text = GetInvitationCodeByPlayFabID();
|
||||
|
||||
|
||||
scroller = transform.Find("list/ScrollView").GetComponent<EnhancedScroller>();
|
||||
scroller.Delegate = this;
|
||||
|
||||
m_RecommendedBackendOperationList.Clear();
|
||||
m_InvitedBackendOperationList.Clear();
|
||||
}
|
||||
|
||||
void ShowGiftIcon()
|
||||
{
|
||||
List<cfg.Invite> invites = GContext.container.Resolve<Tables>().TbInvite.DataList;
|
||||
//已经领取奖励的数量
|
||||
string receiveRewardCountStr = saveStore.Read(FriendService.InviteLinkRewardCountKey).ValueOrThrow();
|
||||
if (receiveRewardCountStr != null)
|
||||
{
|
||||
int.TryParse(receiveRewardCountStr, out int InviteLinkRewardCount);
|
||||
icon_gift_invite.SetActive(InviteLinkRewardCount < invites.Count);
|
||||
}
|
||||
}
|
||||
|
||||
void OnInviteClick()
|
||||
{
|
||||
_ = UIManager.Instance.ShowUI(UITypes.FishingSocialInviteFriendsPopupPanel);
|
||||
}
|
||||
|
||||
async void OnAddClick()
|
||||
{
|
||||
await UIManager.Instance.ShowUI(UITypes.FishingSocialFriendSearchPanel);
|
||||
}
|
||||
async void OnEnable()
|
||||
{
|
||||
m_PageNo = 0;
|
||||
ShowGiftIcon();
|
||||
//if (scroller != null) scroller.gameObject.SetActive(false);
|
||||
if (empty != null) empty.SetActive(false);
|
||||
loading.SetActive(true);
|
||||
await InitDefault();
|
||||
|
||||
loading.SetActive(false);
|
||||
}
|
||||
private void Update()
|
||||
{
|
||||
UpdateLockAction(Time.deltaTime);
|
||||
}
|
||||
protected void OnDestroy()
|
||||
{
|
||||
foreach (var item in m_RecommendedBackendOperationList)
|
||||
{
|
||||
item.Clear();
|
||||
}
|
||||
m_RecommendedBackendOperationList.Clear();
|
||||
|
||||
foreach (var item in m_InvitedBackendOperationList)
|
||||
{
|
||||
item.Clear();
|
||||
}
|
||||
m_InvitedBackendOperationList.Clear();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
using UnityEngine;
|
||||
using asap.core;
|
||||
using System.Text;
|
||||
using cfg;
|
||||
using System.Linq;
|
||||
using GameCore;
|
||||
using game;
|
||||
|
||||
namespace Assets.Scripts.UI.SocialRush
|
||||
{
|
||||
public class SocialRushArchitecture
|
||||
{
|
||||
readonly game.PlayerDataStorage.IPlayerSaveStore saveStore = GContext.container.Resolve<game.PlayerDataStorage.IPlayerSaveStore>();
|
||||
|
||||
private Model _model = new Model();
|
||||
private TableContext _tableContext;
|
||||
public int EventId => _tableContext.EventId;
|
||||
public Model Model => _model;
|
||||
public TableContext TableContext => _tableContext;
|
||||
|
||||
#region RedPoint
|
||||
private bool _isFirstTime = true;
|
||||
public bool IsFirstTime => _isFirstTime;
|
||||
private string _redPointKey = "social_rush";
|
||||
public string RedPointKey => _redPointKey;
|
||||
#endregion
|
||||
|
||||
#region DataStorage
|
||||
public static void Load(FishingEvent e)
|
||||
{
|
||||
var arc = GContext.container.Resolve<SocialRushArchitecture>();
|
||||
if (arc == null)
|
||||
{
|
||||
GContext.container.Register<SocialRushArchitecture>().AsSingleton();
|
||||
arc = GContext.container.Resolve<SocialRushArchitecture>();
|
||||
}
|
||||
var res = arc.LoadPlayfab();
|
||||
arc.LoadPlayerPreferences();
|
||||
if (!res || arc.EventId != e.ID)
|
||||
arc.Init(e.ID);
|
||||
var urls = arc.TableContext.GetAddressableUrls();
|
||||
UITypes.SocialRushPanel.SetType(urls[1]);
|
||||
}
|
||||
|
||||
public void InitTest(int eventId)
|
||||
{
|
||||
_tableContext = TableContext.CreateTest(eventId);
|
||||
_model.Reset();
|
||||
_model.Set(0, 0, GContext.container.Resolve<GameCore.PlayerData>().InflationRate);
|
||||
}
|
||||
|
||||
public void Init(int eventId)
|
||||
{
|
||||
_tableContext = new TableContext(eventId);
|
||||
_model.Reset();
|
||||
_isFirstTime = true;
|
||||
SavePlayfab();
|
||||
SavePlayerPreferences();
|
||||
}
|
||||
|
||||
private const char Pipe = '|';
|
||||
private const string PfKey = "FeastRush";
|
||||
private string PpKey => "SocialRush" + GContext.container.Resolve<IUserService>().UserId;
|
||||
public void SavePlayfab()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(_tableContext.EventId).Append(Pipe);
|
||||
sb.Append(_model.ProgressDisplay).Append(Pipe);
|
||||
sb.Append(_model.ProgressActual).Append(Pipe);
|
||||
sb.Append(_model.InflationRate).Append(Pipe);
|
||||
saveStore.Write(PfKey, sb.ToString()).RequireAccepted();
|
||||
}
|
||||
|
||||
public bool LoadPlayfab()
|
||||
{
|
||||
try
|
||||
{
|
||||
var dataString = saveStore.Read(PfKey).ValueOrThrow();
|
||||
var tokens = dataString.Trim(Pipe).Split(Pipe);
|
||||
var eventId = int.Parse(tokens[0]);
|
||||
var progressDisplay = int.Parse(tokens[1]);
|
||||
var progressActual = int.Parse(tokens[2]);
|
||||
var inflationRate = float.Parse(tokens[3]);
|
||||
_tableContext = new TableContext(eventId);
|
||||
_model.Set(progressActual, progressDisplay, inflationRate);
|
||||
return true;
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogWarning(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadPlayerPreferences()
|
||||
{
|
||||
_isFirstTime = PlayerPrefs.GetInt(PpKey, 1) == 1;
|
||||
}
|
||||
|
||||
public void SavePlayerPreferences()
|
||||
{
|
||||
PlayerPrefs.SetInt(PpKey, _isFirstTime ? 1 : 0);
|
||||
}
|
||||
|
||||
public void SetFirstTime()
|
||||
{
|
||||
_isFirstTime = false;
|
||||
SavePlayerPreferences();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System
|
||||
public bool IsActive()
|
||||
{
|
||||
return _tableContext.IsActive()
|
||||
&& _model.ProgressDisplay < _tableContext.GetTaskList().Sum(x => x.TaskParam)
|
||||
&& _model.ProgressDisplay >= 0;
|
||||
}
|
||||
|
||||
public bool NeedUpdate()
|
||||
{
|
||||
return _model.ProgressDisplay < _model.ProgressActual;
|
||||
}
|
||||
|
||||
public bool NeedPopup()
|
||||
{
|
||||
var res = TableContext.HasUnclaimedReward(_model.ProgressDisplay, _model.ProgressActual, out var displayTaskIdx, out var actualTaskIdx);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void AddProgress(int n)
|
||||
{
|
||||
if (!IsActive())
|
||||
return;
|
||||
_model.AddProgress(n);
|
||||
SavePlayfab();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region leyuan
|
||||
public void AddFishRaidProgress(int multiplier = 1)
|
||||
{
|
||||
var count = _tableContext.GetFishRaidProgress() * multiplier;
|
||||
GContext.Publish(new game.TargetAddData(TableContext.GetProgressItemId(), Model.ProgressActual, count));
|
||||
AddProgress(count);
|
||||
}
|
||||
|
||||
public void AddBombProgress(int multiplier = 1)
|
||||
{
|
||||
var count = _tableContext.GetBombProgress() * multiplier;
|
||||
GContext.Publish(new game.TargetAddData(TableContext.GetProgressItemId(), Model.ProgressActual, count));
|
||||
AddProgress(_tableContext.GetBombProgress() * multiplier);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user