Files
ft/Client/Assets/Scripts/Activity/HomeEntranceBtn/AbstractHomeEntranceBtn.cs
2026-06-29 21:18:33 +08:00

235 lines
7.9 KiB
C#
Raw Permalink 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 System;
using System.Collections.Generic;
using asap.core;
using cfg;
using TimeManager;
using TMPro;
using UniRx;
using UnityEngine;
using UnityEngine.UI;
namespace Activity.HomeEntranceBtn
{
public abstract class AbstractHomeEntranceBtn<TData> : EventButtonResource,IHomeEntranceBtn where TData : IEventData,new ()
{
#region abstract
/// <summary>
/// 关联的 Sentry 类型,子类必须实现
/// </summary>
protected abstract Type AssociatedSentryType { get; }
protected abstract void OnClickBtn();
protected abstract string GetIconName();
/// <summary>
/// 检查并预加载活动资源(子类实现)
/// </summary>
protected abstract List<string> GetResourceNames();
#endregion
#region Data
protected AEventDataManager _manager;
private TData _data;
/// <summary>
/// 数据属性,子类无需重写,基类自动处理新账号无数据的情况
/// </summary>
protected virtual TData Data
{
get
{
if(_manager!=null)
_data = _manager.LoadData<TData>();
return _data;
}
}
#endregion
#region UIElement
[SerializeField] protected TMP_Text textTimer;
[SerializeField] protected Button btn;
[SerializeField] protected Image icon;
#endregion
protected CompositeDisposable _disposable;
// 缓存数据对象,避免每秒创建新对象
protected TData _cachedData;
protected virtual void Awake()
{
if (!textTimer)
textTimer = gameObject.FindChildGameObject("text_time").GetComponent<TMP_Text>();
if (!btn)
btn = gameObject.GetComponent<Button>();
if (!icon)
icon = gameObject.FindChildGameObject("icon_task").GetComponent<Image>();
gameObject.SetActive(false);
btn.onClick.AddListener(OnClickBtn);
RegisterBtnInSentry();
}
protected override void OnDestroy()
{
_data = default;
_disposable?.Dispose();
UnRegisterBtnInSentry();
base.OnDestroy();
}
protected virtual void UpdateTimer(long _ = 0L)
{
gameObject.SetActive(IsActivityActive());
textTimer.text = ConvertTools.ConvertTime2(_cachedData?.RemainingTime ?? TimeSpan.Zero);
}
protected virtual bool IsActivityActive()
{
if (_cachedData == null)
return Data?.IsActive ?? false;
return _cachedData?.IsActive ?? false;
}
protected bool IsCompletedLoadResource = false;
protected override async void OnLoadEventResource()
{
try
{
// 如果已经订阅过,不再重复订阅
if (_disposable == null)
{
_disposable = new CompositeDisposable();
GContext.OnEvent<TData>().Subscribe(UpdateData).AddTo(_disposable);
GContext.container.Resolve<ITimeTickService>()?.SecondTick?.Subscribe(UpdateTimer).AddTo(_disposable);
}
_cachedData = Data;
if (_cachedData != null && IsActivityActive())
{
if(IsCompletedLoadResource)
return;
IsCompletedLoadResource = true;
}
UpdateTimer();
await GContext.container.Resolve<IUIService>().SetImageSprite(icon, GetIconName());
}
catch (Exception e)
{
Debug.LogError(e);
}
}
private void UpdateData(TData data)
{
if (data == null || data.EventID != _cachedData.EventID) return;
_cachedData = Data;
UpdateTimer();
}
/// <summary>
/// 活动激活时调用,预加载活动资源
/// </summary>
public virtual void OnOpen(FishingEvent @event,AEventDataManager manager)
{
_manager = manager;
// 具体资源检查由子类实现
var resources = GetResourceNames();
if(resources is { Count: > 0 })
CheckResource(resources, ReChecking: true);
else
OnLoadEventResource();
}
/// <summary>
/// 活动关闭时调用
/// </summary>
public virtual void OnClose(FishingEvent fishingEvent)
{
gameObject.SetActive(false);
_disposable?.Dispose();
_disposable = null;
_manager = null;
_data = default;
_cachedData = default;
IsCompletedLoadResource = false;
}
/// <summary>
/// 从关联的 Sentry 类型中获取类型键(使用缓存,零反射开销)
/// </summary>
private (int EventType, int EventSubType) GetTypeKeyFromSentry()
{
var typeKey = Activity.ActivitySentryManager.GetTypeKey(AssociatedSentryType);
if (typeKey == null)
{
Debug.LogError(
$"[{GetType().Name}] Sentry type {AssociatedSentryType.Name} is not registered in ActivitySentryManager. " +
"Please ensure the Sentry class is decorated with [ActivitySentry(eventType, eventSubType)] " +
"and is being discovered during initialization.");
return default;
}
return typeKey.Value;
}
/// <summary>
/// 注册按钮到 Sentry虚方法子类可重写
/// </summary>
public virtual void RegisterBtnInSentry()
{
try
{
var typeKey = GetTypeKeyFromSentry();
var sentry = GContext.container.Resolve<ActivityManager>().GetSentry(typeKey);
if (sentry == null)
{
Debug.LogWarning($"[{GetType().Name}] No sentry found for event type ({typeKey.EventType}, {typeKey.EventSubType})");
return;
}
if (sentry.HomeEntranceBtn != null && (AbstractHomeEntranceBtn<TData>)sentry.HomeEntranceBtn == this)
{
Debug.LogWarning($"[{GetType().Name}] Sentry already has button registered: {sentry.HomeEntranceBtn.GetType().Name} sentry.HomeEntranceBtn {sentry.HomeEntranceBtn.GetHashCode()} this {GetHashCode()}");
return;
}
sentry.RegisterHomeEntranceBtn(this);
}
catch (Exception ex)
{
Debug.LogError($"[{GetType().Name}] Failed to register in Sentry: {ex.Message}");
}
}
/// <summary>
/// 注册按钮到 Sentry虚方法子类可重写
/// </summary>
public virtual void UnRegisterBtnInSentry()
{
try
{
// 检查容器是否已被销毁
if (GContext.container == null)
return;
var activityManager = GContext.container.Resolve<ActivityManager>();
if (activityManager == null)
return;
var typeKey = GetTypeKeyFromSentry();
var sentry = activityManager.GetSentry(typeKey);
if (sentry == null)
{
Debug.LogWarning($"[{GetType().Name}] No sentry found for event type ({typeKey.EventType}, {typeKey.EventSubType})");
return;
}
sentry.UnregisterHomeEntranceBtn();
}
catch (Exception ex)
{
Debug.LogError($"[{GetType().Name}] Failed to unregister in Sentry: {ex.Message}");
}
}
}
}