429 lines
16 KiB
C#
429 lines
16 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using System.Reflection;
|
|
using BrewMonster.Network;
|
|
using BrewMonster;
|
|
using ModelRenderer.Scripts.GameData;
|
|
|
|
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;
|
|
[SerializeField] private Button equipButton;
|
|
|
|
[Header("Inventory Settings")]
|
|
[SerializeField] private bool autoRefresh = true;
|
|
[SerializeField] private float refreshInterval = 1.0f;
|
|
|
|
private float lastRefreshTime;
|
|
|
|
private InventoryModel model;
|
|
private InventoryView view;
|
|
|
|
// Current selected item for equip/unequip operations
|
|
private byte currentSelectedPackage;
|
|
private int currentSelectedSlot;
|
|
private InventoryItemData currentSelectedItem;
|
|
|
|
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))
|
|
{
|
|
// Store current selection for equip/unequip operations
|
|
currentSelectedPackage = package;
|
|
currentSelectedSlot = slot;
|
|
currentSelectedItem = 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);
|
|
}
|
|
|
|
public void OnEquipButtonClicked()
|
|
{
|
|
if (currentSelectedItem == null)
|
|
{
|
|
Debug.LogWarning("[InventoryUI] No item selected for equip/unequip operation");
|
|
return;
|
|
}
|
|
|
|
if (currentSelectedPackage == PKG_INVENTORY)
|
|
{
|
|
// Equipping from inventory
|
|
EquipItem();
|
|
}
|
|
else if (currentSelectedPackage == PKG_EQUIPMENT)
|
|
{
|
|
// Unequipping from equipment
|
|
UnequipItem();
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"[InventoryUI] Equip/Unequip not supported for package {currentSelectedPackage}");
|
|
}
|
|
}
|
|
|
|
private void EquipItem()
|
|
{
|
|
if (currentSelectedItem == null) return;
|
|
|
|
// For equipping, we need to find an empty equipment slot
|
|
// The equip location should be determined by the item type or use a default
|
|
byte equipLocation = EC_IvtrType.GetEquipLocationForItem(currentSelectedItem.TemplateId);
|
|
if (equipLocation >= (byte)EC_IvtrType.IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR)
|
|
{
|
|
Debug.LogWarning($"[InventoryUI] Could not determine equip location for item {currentSelectedItem.TemplateId}");
|
|
return;
|
|
}
|
|
|
|
// Call RequestEquipItemAsync with inventory slot and equip location
|
|
UnityGameSession.RequestEquipItemAsync((byte)currentSelectedSlot, equipLocation, () =>
|
|
{
|
|
Debug.Log($"[InventoryUI] Equip request sent for item {currentSelectedItem.TemplateId} from slot {currentSelectedSlot} to equip location {equipLocation}");
|
|
// Refresh inventory after equip
|
|
RefreshAll();
|
|
});
|
|
}
|
|
|
|
private void UnequipItem()
|
|
{
|
|
if (currentSelectedItem == null) return;
|
|
|
|
// Find empty slot in PACK_INVENTORY
|
|
int emptySlot = FindEmptyInventorySlot();
|
|
if (emptySlot == -1)
|
|
{
|
|
Debug.LogWarning("[InventoryUI] No empty slots available in inventory for unequipping");
|
|
return;
|
|
}
|
|
|
|
// For unequipping, the equip location is the current equipment slot
|
|
// We can use the slot number as the equip location
|
|
byte equipLocation = (byte)currentSelectedSlot;
|
|
|
|
// Call RequestEquipItemAsync with empty inventory slot and current equip location
|
|
UnityGameSession.RequestEquipItemAsync((byte)emptySlot, equipLocation, () =>
|
|
{
|
|
Debug.Log($"[InventoryUI] Unequip request sent for item {currentSelectedItem.TemplateId} from equip location {equipLocation} to inventory slot {emptySlot}");
|
|
// Refresh inventory after unequip
|
|
RefreshAll();
|
|
});
|
|
}
|
|
|
|
|
|
|
|
private byte GetDefaultEquipLocation(int templateId)
|
|
{
|
|
// Basic equip location mapping based on template ID
|
|
// Adjust these ranges based on your game's item system
|
|
if (templateId >= 1000 && templateId < 2000) return 0; // Weapon
|
|
if (templateId >= 2000 && templateId < 3000) return 2; // Armor
|
|
if (templateId >= 3000 && templateId < 4000) return 3; // Accessory
|
|
if (templateId >= 4000 && templateId < 5000) return 4; // Ring
|
|
if (templateId >= 5000 && templateId < 6000) return 5; // Necklace
|
|
|
|
// Default to slot 1 if no specific mapping
|
|
Debug.Log($"[InventoryUI] Using default equip location 1 for template {templateId}");
|
|
return 0;
|
|
}
|
|
|
|
private int FindEmptyInventorySlot()
|
|
{
|
|
var inventoryData = model.GetInventoryData(PKG_INVENTORY);
|
|
if (inventoryData == null) return -1;
|
|
|
|
// Find first empty slot (assuming slots are numbered 0, 1, 2, ...)
|
|
for (int i = 0; i < 100; i++) // Assuming max 100 inventory slots
|
|
{
|
|
if (!inventoryData.ContainsKey(i))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Equipment indices and resolution helpers moved to EC_IvtrType
|
|
|
|
// === 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());
|
|
|
|
// Setup equip button
|
|
SetupEquipButton(package, item);
|
|
|
|
ShowDetailPanel(true);
|
|
}
|
|
|
|
private void SetupEquipButton(byte package, InventoryItemData item)
|
|
{
|
|
if (equipButton == null) return;
|
|
|
|
// Clear previous listeners
|
|
equipButton.onClick.RemoveAllListeners();
|
|
equipButton.onClick.AddListener(OnEquipButtonClicked);
|
|
|
|
// Set button text and visibility based on package
|
|
var buttonText = equipButton.GetComponentInChildren<UnityEngine.UI.Text>();
|
|
if (buttonText == null)
|
|
{
|
|
var tmpText = equipButton.GetComponentInChildren<TMPro.TextMeshProUGUI>();
|
|
if (tmpText != null)
|
|
{
|
|
if (package == PKG_INVENTORY)
|
|
{
|
|
tmpText.text = "Equip";
|
|
equipButton.gameObject.SetActive(true);
|
|
}
|
|
else if (package == PKG_EQUIPMENT)
|
|
{
|
|
tmpText.text = "UnEquip";
|
|
equipButton.gameObject.SetActive(true);
|
|
}
|
|
else
|
|
{
|
|
equipButton.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (package == PKG_INVENTORY)
|
|
{
|
|
buttonText.text = "Equip";
|
|
equipButton.gameObject.SetActive(true);
|
|
}
|
|
else if (package == PKG_EQUIPMENT)
|
|
{
|
|
buttonText.text = "UnEquip";
|
|
equipButton.gameObject.SetActive(true);
|
|
}
|
|
else
|
|
{
|
|
equipButton.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
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}";
|
|
}
|
|
}
|
|
}
|
|
}
|