344 lines
11 KiB
C#
344 lines
11 KiB
C#
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
|
||
{
|
||
#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()
|
||
{
|
||
PlayFabMgr.Instance.UpdateUserDataValue(PlayfabKey, $"{_eventId}|{_tokenGiven}|{_currentScore}");
|
||
}
|
||
|
||
public bool LoadData()
|
||
{
|
||
var s = PlayFabMgr.Instance.GetLocalData(PlayfabKey);
|
||
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
|
||
}
|