Update EC_InventoryUI.cs
This commit is contained in:
@@ -1,238 +1,255 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.Reflection;
|
||||
|
||||
namespace PerfectWorld.Scripts.Managers
|
||||
{
|
||||
public class EC_InventoryUI : MonoBehaviour
|
||||
{
|
||||
[Header("UI References")]
|
||||
[SerializeField] private GridLayoutGroup gridLayoutGroup;
|
||||
[SerializeField] private GameObject inventoryButtonPrefab;
|
||||
[SerializeField] private Transform inventoryContainer;
|
||||
|
||||
[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 byte targetPackage = 0; // 0 = PACK_INVENTORY, 1 = PACK_EQUIPMENT, 2 = PACK_TASKINVENTORY
|
||||
[SerializeField] private bool autoRefresh = true;
|
||||
[SerializeField] private float refreshInterval = 1.0f;
|
||||
|
||||
private List<GameObject> inventoryButtons = new List<GameObject>();
|
||||
|
||||
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()
|
||||
{
|
||||
InitializeUI();
|
||||
RefreshInventory();
|
||||
RefreshAll();
|
||||
if (hideDetailOnStart)
|
||||
{
|
||||
ShowDetailPanel(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (autoRefresh && Time.time - lastRefreshTime >= refreshInterval)
|
||||
{
|
||||
RefreshInventory();
|
||||
RefreshAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeUI()
|
||||
{
|
||||
if (gridLayoutGroup == null)
|
||||
{
|
||||
Debug.LogError("[InventoryUI] GridLayoutGroup is not assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (inventoryButtonPrefab == null)
|
||||
{
|
||||
Debug.LogError("[InventoryUI] Inventory Button Prefab is not assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (inventoryContainer == null)
|
||||
{
|
||||
inventoryContainer = gridLayoutGroup.transform;
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshInventory()
|
||||
|
||||
public void RefreshAll()
|
||||
{
|
||||
lastRefreshTime = Time.time;
|
||||
ClearInventoryButtons();
|
||||
|
||||
// Get inventory data from EC_Inventory
|
||||
var inventoryData = GetInventoryData(targetPackage);
|
||||
|
||||
if (inventoryData == null || inventoryData.Count == 0)
|
||||
{
|
||||
Debug.Log($"[InventoryUI] No items found in package {targetPackage}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create buttons for each inventory item
|
||||
foreach (var item in inventoryData.Values.OrderBy(x => x.Slot))
|
||||
{
|
||||
CreateInventoryButton(item);
|
||||
}
|
||||
|
||||
Debug.Log($"[InventoryUI] Refreshed inventory with {inventoryData.Count} items");
|
||||
|
||||
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 Dictionary<int, InventoryItemData> GetInventoryData(byte package)
|
||||
|
||||
private void OnInventoryButtonClicked(byte package, int slot)
|
||||
{
|
||||
// Access the private _itemsByPackage dictionary from EC_Inventory using reflection
|
||||
var inventoryType = typeof(EC_Inventory);
|
||||
var itemsByPackageField = inventoryType.GetField("_itemsByPackage",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
|
||||
|
||||
if (itemsByPackageField == null)
|
||||
var data = model.GetInventoryData(package);
|
||||
if (data != null && data.TryGetValue(slot, out var itemData))
|
||||
{
|
||||
Debug.LogError("[InventoryUI] Could not access _itemsByPackage field from EC_Inventory");
|
||||
return new Dictionary<int, InventoryItemData>();
|
||||
FillDetailPanel(package, itemData);
|
||||
}
|
||||
|
||||
var itemsByPackage = itemsByPackageField.GetValue(null) as Dictionary<byte, Dictionary<int, InventoryItemData>>;
|
||||
if (itemsByPackage == null)
|
||||
else
|
||||
{
|
||||
Debug.LogError("[InventoryUI] _itemsByPackage is null");
|
||||
return new Dictionary<int, InventoryItemData>();
|
||||
}
|
||||
|
||||
if (itemsByPackage.TryGetValue(package, out var packageItems))
|
||||
{
|
||||
return packageItems;
|
||||
}
|
||||
|
||||
return new Dictionary<int, InventoryItemData>();
|
||||
}
|
||||
|
||||
private void CreateInventoryButton(InventoryItemData itemData)
|
||||
{
|
||||
if (inventoryButtonPrefab == null) return;
|
||||
|
||||
// Instantiate button
|
||||
GameObject buttonObj = Instantiate(inventoryButtonPrefab, inventoryContainer);
|
||||
inventoryButtons.Add(buttonObj);
|
||||
|
||||
// Get button components
|
||||
Button button = buttonObj.GetComponent<Button>();
|
||||
if (button == null)
|
||||
{
|
||||
Debug.LogWarning("[InventoryUI] Button component not found on prefab");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up button text
|
||||
SetupButtonText(buttonObj, itemData);
|
||||
|
||||
// Set up button click handler
|
||||
button.onClick.RemoveAllListeners();
|
||||
button.onClick.AddListener(() => OnInventoryItemClicked(itemData));
|
||||
|
||||
// Set up button visual state
|
||||
SetupButtonVisual(buttonObj, itemData);
|
||||
}
|
||||
|
||||
private void SetupButtonText(GameObject buttonObj, InventoryItemData itemData)
|
||||
{
|
||||
// Try to find Text component (for older UI system)
|
||||
Text textComponent = buttonObj.GetComponentInChildren<Text>();
|
||||
if (textComponent != null)
|
||||
{
|
||||
string itemName = EC_IvtrItem.ResolveItemName(itemData.TemplateId);
|
||||
string displayText = string.IsNullOrEmpty(itemName) ?
|
||||
$"Item {itemData.TemplateId}" : itemName;
|
||||
|
||||
if (itemData.Count > 1)
|
||||
{
|
||||
displayText += $" x{itemData.Count}";
|
||||
}
|
||||
|
||||
textComponent.text = displayText;
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to find TextMeshProUGUI component (for TextMeshPro)
|
||||
var tmpComponent = buttonObj.GetComponentInChildren<TMPro.TextMeshProUGUI>();
|
||||
if (tmpComponent != null)
|
||||
{
|
||||
string itemName = EC_IvtrItem.ResolveItemName(itemData.TemplateId);
|
||||
string displayText = string.IsNullOrEmpty(itemName) ?
|
||||
$"Item {itemData.TemplateId}" : itemName;
|
||||
|
||||
if (itemData.Count > 1)
|
||||
{
|
||||
displayText += $" x{itemData.Count}";
|
||||
}
|
||||
|
||||
tmpComponent.text = displayText;
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.LogWarning("[InventoryUI] No text component found on button prefab");
|
||||
}
|
||||
|
||||
private void SetupButtonVisual(GameObject buttonObj, InventoryItemData itemData)
|
||||
{
|
||||
// Set button color based on item state or other properties
|
||||
Image buttonImage = buttonObj.GetComponent<Image>();
|
||||
if (buttonImage != null)
|
||||
{
|
||||
// You can customize colors based on item properties
|
||||
if (itemData.State != 0)
|
||||
{
|
||||
buttonImage.color = Color.yellow; // Highlight special state items
|
||||
}
|
||||
else if (itemData.Count <= 0)
|
||||
{
|
||||
buttonImage.color = Color.gray; // Gray out empty items
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonImage.color = Color.white; // Default color
|
||||
}
|
||||
ShowDetailPanel(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInventoryItemClicked(InventoryItemData itemData)
|
||||
|
||||
private string GetDisplayTextForItem(int slot, InventoryItemData itemData)
|
||||
{
|
||||
if (itemData == null || itemData.Count <= 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
string itemName = EC_IvtrItem.ResolveItemName(itemData.TemplateId);
|
||||
Debug.Log($"[InventoryUI] Item clicked: {itemName} (ID: {itemData.TemplateId}, Count: {itemData.Count}, Slot: {itemData.Slot})");
|
||||
|
||||
// Add your custom item click logic here
|
||||
// For example: show item details, use item, etc.
|
||||
}
|
||||
|
||||
private void ClearInventoryButtons()
|
||||
{
|
||||
foreach (GameObject button in inventoryButtons)
|
||||
string displayText = string.IsNullOrEmpty(itemName) ? $"Item {itemData.TemplateId}" : itemName;
|
||||
if (itemData.Count > 1)
|
||||
{
|
||||
if (button != null)
|
||||
{
|
||||
DestroyImmediate(button);
|
||||
}
|
||||
displayText += $" x{itemData.Count}";
|
||||
}
|
||||
inventoryButtons.Clear();
|
||||
return displayText;
|
||||
}
|
||||
|
||||
public void SetTargetPackage(byte package)
|
||||
{
|
||||
targetPackage = package;
|
||||
RefreshInventory();
|
||||
}
|
||||
|
||||
|
||||
public void ToggleAutoRefresh()
|
||||
{
|
||||
autoRefresh = !autoRefresh;
|
||||
}
|
||||
|
||||
|
||||
public void SetRefreshInterval(float interval)
|
||||
{
|
||||
refreshInterval = Mathf.Max(0.1f, interval);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
|
||||
// === MVC: Model ===
|
||||
private class InventoryModel
|
||||
{
|
||||
ClearInventoryButtons();
|
||||
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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user