Files
ft/Client/Assets/Scripts/UI/PartnerStatue/EventPartnerStatueTokenExchangeData.cs
2026-06-29 21:18:33 +08:00

344 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}