256 lines
8.9 KiB
C#
256 lines
8.9 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using System.Reflection;
|
|
|
|
namespace PerfectWorld.Scripts.Managers
|
|
{
|
|
public class EC_InventoryUI : MonoBehaviour
|
|
{
|
|
[Header("Pack Buttons (assign in Inspector)")]
|
|
[SerializeField] private List<Button> inventoryPackButtons = new List<Button>(); // byPackage: 0
|
|
[SerializeField] private List<Button> equipmentPackButtons = new List<Button>(); // byPackage: 1
|
|
[SerializeField] private List<Button> fashionPackButtons = new List<Button>(); // byPackage: 3
|
|
|
|
[Header("Detail Panel (assign in Inspector)")]
|
|
[SerializeField] private GameObject detailPanelRoot;
|
|
[SerializeField] private bool hideDetailOnStart = true;
|
|
[SerializeField] private TextOutlet nameText;
|
|
[SerializeField] private TextOutlet templateIdText;
|
|
[SerializeField] private TextOutlet countText;
|
|
[SerializeField] private TextOutlet slotText;
|
|
[SerializeField] private TextOutlet packageText;
|
|
[SerializeField] private TextOutlet stateText;
|
|
[SerializeField] private TextOutlet expireText;
|
|
[SerializeField] private TextOutlet crcText;
|
|
[SerializeField] private TextOutlet contentLenText;
|
|
|
|
[Header("Inventory Settings")]
|
|
[SerializeField] private bool autoRefresh = true;
|
|
[SerializeField] private float refreshInterval = 1.0f;
|
|
|
|
private float lastRefreshTime;
|
|
|
|
private InventoryModel model;
|
|
private InventoryView view;
|
|
|
|
private const byte PKG_INVENTORY = 0;
|
|
private const byte PKG_EQUIPMENT = 1;
|
|
private const byte PKG_FASHION = 3; // Note: byPackage 3 used for Fashion
|
|
|
|
private void Awake()
|
|
{
|
|
model = new InventoryModel();
|
|
view = new InventoryView();
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
RefreshAll();
|
|
if (hideDetailOnStart)
|
|
{
|
|
ShowDetailPanel(false);
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (autoRefresh && Time.time - lastRefreshTime >= refreshInterval)
|
|
{
|
|
RefreshAll();
|
|
}
|
|
}
|
|
|
|
public void RefreshAll()
|
|
{
|
|
lastRefreshTime = Time.time;
|
|
|
|
var invItems = model.GetInventoryData(PKG_INVENTORY);
|
|
var eqpItems = model.GetInventoryData(PKG_EQUIPMENT);
|
|
var fshItems = model.GetInventoryData(PKG_FASHION);
|
|
|
|
view.RenderPackage(inventoryPackButtons, invItems, PKG_INVENTORY, OnInventoryButtonClicked, GetDisplayTextForItem);
|
|
view.RenderPackage(equipmentPackButtons, eqpItems, PKG_EQUIPMENT, OnInventoryButtonClicked, GetDisplayTextForItem);
|
|
view.RenderPackage(fashionPackButtons, fshItems, PKG_FASHION, OnInventoryButtonClicked, GetDisplayTextForItem);
|
|
}
|
|
|
|
private void OnInventoryButtonClicked(byte package, int slot)
|
|
{
|
|
var data = model.GetInventoryData(package);
|
|
if (data != null && data.TryGetValue(slot, out var itemData))
|
|
{
|
|
FillDetailPanel(package, itemData);
|
|
}
|
|
else
|
|
{
|
|
ShowDetailPanel(false);
|
|
}
|
|
}
|
|
|
|
private string GetDisplayTextForItem(int slot, InventoryItemData itemData)
|
|
{
|
|
if (itemData == null || itemData.Count <= 0)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
string itemName = EC_IvtrItem.ResolveItemName(itemData.TemplateId);
|
|
string displayText = string.IsNullOrEmpty(itemName) ? $"Item {itemData.TemplateId}" : itemName;
|
|
if (itemData.Count > 1)
|
|
{
|
|
displayText += $" x{itemData.Count}";
|
|
}
|
|
return displayText;
|
|
}
|
|
|
|
public void ToggleAutoRefresh()
|
|
{
|
|
autoRefresh = !autoRefresh;
|
|
}
|
|
|
|
public void SetRefreshInterval(float interval)
|
|
{
|
|
refreshInterval = Mathf.Max(0.1f, interval);
|
|
}
|
|
|
|
// === MVC: Model ===
|
|
private class InventoryModel
|
|
{
|
|
private readonly FieldInfo itemsByPackageField;
|
|
|
|
public InventoryModel()
|
|
{
|
|
var inventoryType = typeof(EC_Inventory);
|
|
itemsByPackageField = inventoryType.GetField("_itemsByPackage", BindingFlags.NonPublic | BindingFlags.Static);
|
|
if (itemsByPackageField == null)
|
|
{
|
|
Debug.LogError("[InventoryUI] Could not access _itemsByPackage field from EC_Inventory");
|
|
}
|
|
}
|
|
|
|
public Dictionary<int, InventoryItemData> GetInventoryData(byte package)
|
|
{
|
|
if (itemsByPackageField == null)
|
|
{
|
|
return new Dictionary<int, InventoryItemData>();
|
|
}
|
|
var itemsByPackage = itemsByPackageField.GetValue(null) as Dictionary<byte, Dictionary<int, InventoryItemData>>;
|
|
if (itemsByPackage == null)
|
|
{
|
|
return new Dictionary<int, InventoryItemData>();
|
|
}
|
|
if (itemsByPackage.TryGetValue(package, out var packageItems))
|
|
{
|
|
return packageItems;
|
|
}
|
|
return new Dictionary<int, InventoryItemData>();
|
|
}
|
|
}
|
|
|
|
// === MVC: View ===
|
|
private class InventoryView
|
|
{
|
|
public void RenderPackage(List<Button> buttons, Dictionary<int, InventoryItemData> items, byte package, System.Action<byte, int> onClick, System.Func<int, InventoryItemData, string> getDisplayText)
|
|
{
|
|
if (buttons == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int slot = 0; slot < buttons.Count; slot++)
|
|
{
|
|
var button = buttons[slot];
|
|
if (button == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
InventoryItemData itemData = null;
|
|
bool hasItem = items != null && items.TryGetValue(slot, out itemData);
|
|
button.onClick.RemoveAllListeners();
|
|
int capturedSlot = slot;
|
|
button.onClick.AddListener(() => onClick(package, capturedSlot));
|
|
|
|
// Optional visual tweaks based on state/count
|
|
var image = button.GetComponent<Image>();
|
|
if (image != null)
|
|
{
|
|
if (hasItem && itemData != null && itemData.State != 0)
|
|
{
|
|
image.color = Color.yellow;
|
|
}
|
|
else if (!hasItem)
|
|
{
|
|
image.color = Color.white;
|
|
}
|
|
else
|
|
{
|
|
image.color = Color.white;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// === Detail Panel Helpers ===
|
|
[System.Serializable]
|
|
private class TextOutlet
|
|
{
|
|
[SerializeField] public Text legacy;
|
|
[SerializeField] public TMPro.TextMeshProUGUI tmp;
|
|
|
|
public void Set(string value)
|
|
{
|
|
if (legacy != null)
|
|
{
|
|
legacy.text = value ?? string.Empty;
|
|
}
|
|
if (tmp != null)
|
|
{
|
|
tmp.text = value ?? string.Empty;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ShowDetailPanel(bool show)
|
|
{
|
|
if (detailPanelRoot != null)
|
|
{
|
|
detailPanelRoot.SetActive(show);
|
|
}
|
|
}
|
|
|
|
private void FillDetailPanel(byte package, InventoryItemData item)
|
|
{
|
|
if (item == null)
|
|
{
|
|
ShowDetailPanel(false);
|
|
return;
|
|
}
|
|
|
|
string name = EC_IvtrItem.ResolveItemName(item.TemplateId);
|
|
nameText?.Set(string.IsNullOrEmpty(name) ? $"Item {item.TemplateId}" : name);
|
|
templateIdText?.Set(item.TemplateId.ToString());
|
|
countText?.Set(item.Count.ToString());
|
|
slotText?.Set(item.Slot.ToString());
|
|
packageText?.Set(GetPackageName(package));
|
|
stateText?.Set(item.State.ToString());
|
|
expireText?.Set(item.ExpireDate.ToString());
|
|
crcText?.Set(item.Crc.ToString());
|
|
contentLenText?.Set((item.Content?.Length ?? 0).ToString());
|
|
|
|
ShowDetailPanel(true);
|
|
}
|
|
|
|
private static string GetPackageName(byte pkg)
|
|
{
|
|
switch (pkg)
|
|
{
|
|
case 0: return "PACK_INVENTORY";
|
|
case 1: return "PACK_EQUIPMENT";
|
|
case 3: return "PACK_FASHION";
|
|
default: return $"PACK_{pkg}";
|
|
}
|
|
}
|
|
}
|
|
}
|